在块上进行分组可能会导致组之间分组

时间:2015-03-27 19:10:34

标签: pandas

由于尺寸的原因,我需要在块中阅读一些非常棒的文件。

我想对这些文件执行groupby然后执行一个函数。

问题是,如果chunksize是50 000并且某个组存在于行49998-50002,则该组将被拆分为两个;第一个块中的一个组,第二个块中的另一个组。有没有办法解决组块之间存在的组的问题?

我能提出的所有解决方案都感觉un-Pandaish所以也许我应该通过两次阅读来解决这个问题。

1 个答案:

答案 0 :(得分:2)

我知道没有开箱即用的功能,但是你可以自己动手:

remainder = pd.DataFrame()
for filename in filenames:                                            # 1
    for chunk in pd.read_csv(filename, index_col=[0], chunksize=300): # 2 
        grouped = chunk.groupby(['group'])
        for grp, nextgrp in iwindow(grouped, 2):                      # 3
            group_num, df = grp                                       # 4
            if nextgrp is None:
                # When nextgrp is None, grp is the last group
                remainder = pd.concat([remainder, df])                # 5
                break                                                 # 6
            if len(remainder):                                        # 7
                df = pd.concat([remainder, df])
                remainder = pd.DataFrame()
            print(filename)
            process(df)                                               # 8
if len(remainder):                                                    # 9         
    process(remainder)
  1. 显然,我们需要迭代每个文件
  2. 以块的形式读取文件。 chunksize=300告诉read_csv以300字节的块读取文件。我为下面的例子做了一些小事。您可以增加此值以在每次迭代时读取更多文件。
  3. iwindow是一个滑动窗口实用程序功能。它一次返回grouped两个项目。例如,

    In [117]: list(iwindow([1,2,3], 2))
    Out[117]: [(1, 2), (2, 3), (3, None)]
    
  4. df是一个具有常量group值(等于group_num)的DataFrame。

  5. 不要处理最后一个DataFrame,因为它可能是部分DataFrame,其中有更多来自下一个块。将其保存在remainder
  6. 突破内循环。继续下一个块(如果有的话)。
  7. 如果remainder包含一些未经处理的DataFrame,请将其添加到df
  8. 最后,处理df
  9. remainder可能会保留最后一个未经处理的DataFrame。所以现在就处理它。
  10. 当您需要以块的形式读取文件但根据其他一些分隔符处理块时,此处使用的一般概念非常有用。 基本上是same idea is used here将文件分成由正则表达式模式分隔的块。


    例如,

    import itertools as IT
    import numpy as np
    import pandas as pd
    
    def make_data(groupsize, ngroups, filenames):
        nfiles = len(filenames)
        group_num = np.repeat(np.arange(ngroups), groupsize) 
        arr = np.random.randint(10, size=(len(group_num), 2))
        arr = np.column_stack([group_num, arr])
        for arri, filename in zip(np.array_split(arr, nfiles), filenames):
            df = pd.DataFrame(arri, columns=['group','A','B'])
            df.to_csv(filename) 
    
    def iwindow(iterable, n=2, fillvalue=None):
        """
        Returns a sliding window (of width n) over data from the sequence.
        s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...
        """
        iterables = IT.tee(iterable, n)
        iterables = (IT.islice(it, pos, None) for pos, it in enumerate(iterables))
        for result in IT.izip_longest(*iterables, fillvalue=None):
            yield result
    
    def process(df):
        print(df)
        print('-'*80)
    
    filenames = ['/tmp/data-{}.csv'.format(i) for i in range(3)]
    make_data(groupsize=40, ngroups=5, filenames=filenames)
    
    remainder = pd.DataFrame()
    for filename in filenames:
        for chunk in pd.read_csv(filename, index_col=[0], chunksize=300):
            grouped = chunk.groupby(['group'])
            for grp, nextgrp in iwindow(grouped, 2):
                group_num, df = grp
                if nextgrp is None:
                    # When nextgrp is None, grp is the last group
                    remainder = pd.concat([remainder, df])
                    break
                if len(remainder):
                    df = pd.concat([remainder, df])
                    remainder = pd.DataFrame()
                print(filename)
                process(df)
    if len(remainder):
        process(remainder)