如何将dumbo序列文件输入转换为制表符分隔文本

0 投票
3 回答
1078 浏览
提问于 2025-04-15 15:23

我有一个输入,这个输入可能是一个单独的基本数据类型,也可能是一个包含基本数据类型的列表或元组。

我想把它变成一个简单的列表,像这样:

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 个回答

0

我得说,你提到的要求有点奇怪,我觉得用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'
1

根据你重新表述的问题,这个 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。这还假设输入的键总是一个单独的项目或者一个项目列表(而不是一个列表的列表),而且输入的值总是一个项目列表(同样,不是一个列表的列表)。

这样符合你的要求吗?

3

听起来你想用 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))

撰写回答