【问题标题】:How can I group a large dataset如何对大型数据集进行分组
【发布时间】:2010-08-04 08:27:05
【问题描述】:

我有一个包含两列的简单文本文件,都是整数

1 5
1 12
2 5
2 341
2 12

等等..

我需要按第二个值对数据集进行分组, 这样输出将是。

5 1 2
12 1 2
341 2

现在的问题是文件非常大,大约 34 Gb 在大小方面,我尝试编写一个 python 脚本将它们分组到一个字典中,其值是一个整数数组,但仍然需要太长时间。 (我想分配array('i') 并在append 上扩展它们需要很长时间。

我现在正计划编写一个猪脚本,我计划在伪分布式 hadoop 机器(一个 Amazon EC3 高内存大型实例)上运行该脚本。

data = load 'Net.txt';
gdata = Group data by $1; // I know it will lead to 5 (1,5) (2,5) but thats okay for this snippet
store gdata into 'res.txt';

我想知道是否有更简单的方法。

更新: 将这么大的文件保存在内存中是没有问题的,在 python 解决方案的情况下,我计划在第一次运行时进行 4 次运行,下一次运行时只考虑从 1 到 1000 万的第二个 col 值,考虑 1000 万到 2000 万等等。但事实证明这真的很慢。

pig / hadoop 解决方案很有趣,因为它将所有内容都保存在磁盘上 [嗯大部分]。

为了更好地理解这个数据集包含大约 4500 万 twitter 用户的连接信息,文件中的格式意味着第二个数字给出的用户 ID 在第一个之后。

我使用过的解决方案:

class AdjDict(dict):
    """
     A special Dictionary Class to hold adjecancy list
    """
    def __missing__(self, key):
        """
        Missing is changed such that when a key is not found an integer array is initialized
        """
        self.__setitem__(key,array.array('i'))
        return self[key]

Adj= AdjDict()

for line in file("net.txt"):
    entry =  line.strip().split('\t')
    node = int(entry[1])
    follower = int(entry[0])
    if node < 10 ** 6:
        Adj[node].append(follower)

# Code for writting Adj matrix to the file:

【问题讨论】:

  • 这让我想起了我的 SO 问题,stackoverflow.com/questions/3357510/… 听起来我们可能正在做类似的事情。我在 python 中使用 map reduce 构建了这个功能,方法是将它分解为两个 map-reduce 作业。我正在寻找一种方法来在猪身上做到这一点。如果您有兴趣,我很乐意分享我现有的工作。

标签: python data-structures hadoop apache-pig


【解决方案1】:

假设您每行有大约 17 个字符(我随机选择了一个数字以使数学更容易),那么您在此文件中有大约 20 亿条记录。除非您在 64 位系统上使用大量物理内存运行,否则您将试图将所有这些内容保存在单个 dict 中的内存中,从而使您的页面文件死气沉沉。这只是将其作为数据结构读入 - 假设在构建此结构后,您打算用它实际一些事情。

使用如此简单的数据格式,我认为你最好用 C 而不是 Python 来做一些事情。破解这些数据应该不难,而且每个值的开销也会少得多。至少,仅保存 20 亿个 4 字节整数将是 8 Gb(除非您可以对当前列为 1 和 2 的值的可能范围做出一些简化假设 - 如果它们适合一个字节或一个短字节,那么你可以使用更小的 int 变量,这对于这种大小的数据集来说是值得的)。

【讨论】:

  • +1 用于指向 C。对于打开如此大的文件,_FILE_OFFSET_FLAG 可能会很有趣。
【解决方案2】:

如果我必须在我目前的硬件上解决这个问题,我可能会编写一些小程序:

第一个将处理 500 兆字节的文件块,交换列并将结果写入新文件。 (你会得到 70 或更多。)(这不会占用太多内存。)

然后我会在每个小文件上调用操作系统提供的sort(1)。 (这可能需要一些内存。)

然后我会编写一个合并排序程序,它将所有 70 多个子文件中的行合并在一起。 (这不会占用太多内存。)

然后我会编写一个程序来运行大型排序列表;你会有一堆像这样的行:

5 1
5 2
12 1
12 2

你需要返回:

5 1 2
12 1 2

(这不会占用太多内存。)

通过将其分成更小的块,希望您可以将 RSS 降低到适合合理机器的程度——它占用更多磁盘 I/O,但在任何硬件上都没有,交换使用会扼杀在一个大程序中处理此问题的尝试。

【讨论】:

  • 即使我考虑过基于排序的解决方案,但是优雅的方法似乎是通过 hadoop,因为它有一个排序作为中间步骤。
【解决方案3】:

也许您可以对文件进行多次遍历。

执行一系列键,每个键都通过文件,例如,如果您选择的范围大小为 100

第一遍 - 计算出 0-99 的所有键
第二遍 - 计算出 100-199 的所有键
第三遍 - 计算出 200-299 的所有键
第四遍 - 计算出 300-399 的所有键
..等等。

对于您的样本,第一遍将输出

5 1 2
12 1 2

第四遍输出

341 2

选择范围大小,以便您创建的 dict 适合您的 RAM

我不会费心使用多处理来尝试通过使用多个内核来加速它,除非你有一个非常快的硬盘驱动器,这应该是 IO 绑定的,你最终会破坏磁盘

【讨论】:

  • 避免我的解决方案涉及的 70 多个临时文件混乱的好方法——但它只有在密钥不达到 MAX_INT 时才有效。 :)
  • @sarnold,它适用于long。为什么你认为 maxint 是个问题?无论如何,它在 64 位版本上相当大 :) 我希望现在处理 34GB 文件的人正在使用 64 位平台(有大量 RAM)
  • @gnibbler,以 100 为增量从 0 到 2^31 将需要 2100 万次遍历文件。到 100,000 次时,仍需要 214,000 次通过文件。所以,我希望他不必数着数。
  • @sarnold,我们的想法是在不超过可用 RAM 的情况下选择尽可能大的范围。无法从样本数据中看出 100 范围还是 100,000 范围是否更好。不过,要使用 2GB 的 RAM 来处理 34GB 的数据,您可能需要多次通过。
  • 由于第二列的值范围是 0 - 4500 万,我曾尝试使用 1000 万步,可能是因为整个程序是用 Python 编写的,结果很慢。
【解决方案4】:

如果您使用的是 34 GB 的文件,我假设硬盘驱动器在存储和访问时间方面都不是问题。如何按顺序读取对,当您找到对 (x,y) 时,打开文件“x”,附加“y”并关闭文件“x”?最后,每个 Twitter 用户 ID 将拥有一个文件,并且每个文件都包含该文件所连接的所有用户。然后,如果您希望结果采用您指定的输出格式,您可以连接所有这些文件。


不过如此,我确实认为: (a) 对于如此庞大的数据集,exact 分辨率是不合适的,并且 (b) 可能有更好的方法来衡量连接性,所以也许您想告诉我们您的最终目标。

确实,您有一个非常大的图,并且已经设计了许多有效的技术来研究巨大图的形状和属性——这些技术中的大多数都是作为流式在线算法而构建的。

例如,一种称为三角形计数的技术与概率基数估计算法相结合,可以有效且快速地提供有关图表中包含的团的信息。有关三角形计数方面的更好的想法,以及它与图形的关系,请参见例如这个(随机选择)article

【讨论】:

    【解决方案5】:

    我有一个类似的要求,你只需要一个猪语句来删除 5 (1,5) (2,5) 中的冗余。

    a = LOAD 'edgelist' USING PigStorage('\t') AS (user:int,following:int);
    b = GROUP a BY user;
    x = FOREACH b GENERATE group.user, a.following;
    store x INTO 'following-list';
    

    【讨论】:

      猜你喜欢
      • 2018-03-10
      • 2015-03-13
      • 2021-03-30
      • 1970-01-01
      • 2021-07-24
      • 2020-06-27
      • 2016-11-30
      • 2011-10-02
      • 1970-01-01
      相关资源
      最近更新 更多