如何将dumbo序列文件输入转换为制表符分隔文本
我有一个输入,这个输入可能是一个单独的基本数据类型,也可能是一个包含基本数据类型的列表或元组。
我想把它变成一个简单的列表,像这样:
def flatten(values):
return list(values)
正常情况下,我会用 flatten(someiterablethatisn'tastring) 来处理。
但是如果 values = '1234',我会得到 ['1', '2', '3', '4'],但我想要的是 ['1234']。
如果 values = 1,我会得到一个类型错误:'int' 对象不可迭代,但我想要的是 [1]。
有没有什么优雅的方法可以做到这一点?我最终想做的就是 '\t'.join(flatten(values))。
编辑:让我更清楚地解释一下……
我希望使用 dumbo 将一个 hadoop 二进制序列文件转换为一个平坦的以制表符分隔的文本文件。通过输出格式选项 -outputformat text。
Dumbo 是一个用于 hadoop 流处理的 Python 封装。简单来说,我需要写一个映射函数:
def mapper(key, values) #做一些事情 yield k, v
其中 k 是键的第一部分的字符串,value 是一个以制表符分隔的字符串,包含键的其余部分和作为字符串的值。
例如:
input: (123, [1,2,3])
output: ('123', '1\t2\t\t3')
或者更复杂的情况:
input: ([123, 'abc'], [1,2,3])
output: ('123', 'abc\t1\t2\t\t3')
输入的键或值可以是一个基本数据类型,也可以是一个基本数据类型的列表或元组。我想要一个可以处理任何情况的 "flatten" 函数,并返回一个值的列表。
对于输出值,我会这样做:v = '\t'.join(list(str(s) for s in flatten(seq)))
3 个回答
我得说,你提到的要求有点奇怪,我觉得用flatten这个词来描述这种操作不太合适。不过,如果你真的确定这是你想要的,那我可以从你的问题中提炼出以下内容:
>>> import itertools
>>> def to_list_of_strings(input):
... if isinstance(input, basestring): # In Py3k: isinstance(input, str)
... return [input]
... try:
... return itertools.chain(*map(to_list_of_strings, input))
... except TypeError:
... return [str(input)]
...
>>> '\t'.join(to_list_of_strings(8))
'8'
>>> '\t'.join(to_list_of_strings((1, 2)))
'1\t2'
>>> '\t'.join(to_list_of_strings("test"))
'test'
>>> '\t'.join(to_list_of_strings(["test", "test2"]))
'test\ttest2'
>>> '\t'.join(to_list_of_strings(range(4)))
'0\t1\t2\t3'
>>> '\t'.join(to_list_of_strings([1, 2, (3, 4)]))
'1\t2\t3\t4'
根据你重新表述的问题,这个 mapper
函数可能正是你想要的:
def mapper(key, values):
r"""Specification: do some stuff yield k, v where k is a string from the
first part in the key, and value is a tab separated string containing the
rest of the key and the values as strings.
>>> mapper(123, [1,2,3])
('123', '1\t2\t3')
>>> mapper([123, 'abc'], [1,2,3])
('123', 'abc\t1\t2\t3')
"""
if not isinstance(key, list):
key = [key]
k, v = key[0], key[1:]
v.extend(values)
return str(k), '\t'.join(map(str, v))
if __name__ == '__main__':
import doctest
doctest.testmod()
看起来你可能需要把那个 return
改成 yield
。这还假设输入的键总是一个单独的项目或者一个项目列表(而不是一个列表的列表),而且输入的值总是一个项目列表(同样,不是一个列表的列表)。
这样符合你的要求吗?
听起来你想用 itertools.chain()
。不过你需要特别处理字符串,因为字符串其实就是字符的可迭代对象。
更新:
如果你用递归生成器来解决这个问题,会简单很多。试试这个:
def flatten(*seq):
for item in seq:
if isinstance(item, basestring):
yield item
else:
try:
it = iter(item)
except TypeError:
yield item
it = None
if it is not None:
for obj in flatten(it):
yield obj
这个方法返回的是一个迭代器,而不是列表,但它是懒加载的,这可能正是你想要的。如果你真的需要一个列表,只需用 list(flatten(seq))
来替代即可。
更新 2:
正如其他人提到的,如果你真正想做的是把这个传入 str.join()
,那么你需要把所有元素转换成字符串。你可以在我上面的例子中把 yield foo
替换成 yield str(foo)
,或者直接使用下面的代码:
"\t".join(str(o) for o in flatten(seq))