在python中,grouby在一个分块文件上的数百万个键上遇到了问题

在python中,grouby在一个分块文件上的数百万个键上遇到了问题,python,csv,pandas,bigdata,Python,Csv,Pandas,Bigdata,我有一个非常大的CSV文件(数十GB),其中包含以下列的web日志:user\u id,time\u stamp,category\u clicked。我必须建立一个记分器来识别用户喜欢和不喜欢的类别。请注意,我有1000多万用户 我首先将其切成块,并将其存储在名为input.h5的HDFStore中,然后在user\u id上使用groupby 这是我的数据:大约2亿行,1000万个唯一的用户ID user id | timestamp | category_clicked 201405120

我有一个非常大的CSV文件(数十GB),其中包含以下列的web日志:
user\u id
time\u stamp
category\u clicked
。我必须建立一个记分器来识别用户喜欢和不喜欢的类别。请注意,我有1000多万用户

我首先将其切成块,并将其存储在名为
input.h5的
HDFStore
中,然后在
user\u id
上使用
groupby

这是我的数据:大约2亿行,1000万个唯一的用户ID

user id | timestamp | category_clicked
20140512081646222000004-927168801|20140722|7
20140512081714121000004-383009763|20140727|4
201405011348508050000041009490586|20140728|1
20140512081646222000004-927168801|20140724|1
20140501135024818000004-1623130763|20140728|3
这是我的熊猫。show_version():

以下是我想要的输出:

对于每个用户id,一个列表
[0.1,0.45,0.89,1.45,5.12,0,0,0,0.45,0.12,2.36,7.8]
表示每个类别的用户分数和全局分数。我不能告诉你更多关于分数的信息,但它需要计算所有的时间戳和点击的类别。你不能在以后总结或诸如此类的事情

这是我的密码:

clean_input_reader = read_csv(work_path + '/input/input.csv', chunksize=500000)
with get_store(work_path+'/input/input.h5') as store:
    for chunk in clean_input_reader:
        store.append('clean_input', chunk,
                     data_columns=['user_id','timestamp','category_clicked'],
                     min_itemsize=15)

    groups = store.select_column('clean_input','user_id').unique()
    for user in groups:
        group_user = store.select('clean_input',where=['user_id==%s' %user])
        <<<<TREATMENT returns a list user_cat_score>>>>
        store.append(user, Series(user_cat_score))
clean_input_reader=read_csv(工作路径+'/input/input.csv',chunksize=500000)
使用get_store(work_path+'/input/input.h5')作为存储:
对于clean_输入_读取器中的块:
store.append('clean_input',chunk,
数据列=['user\u id'、'timestamp'、'category\u clicked'],
最小项目大小=15)
groups=store.select_列('clean_input','user_id')。unique()
对于组中的用户:
组用户=存储。选择('clean\u input',其中=['user\u id==%s'%user])
store.append(用户,系列(用户分类分数))
我的问题如下:在我看来:
group\u user=store。select('clean\u input',其中=['user\u id==%s'%user])
的时间复杂度太高,因为我有很多组,而且我确信
store的例程中有很多冗余排序。如果我应用它1000万次,请选择

为了给你一个估计,我用这项技术用250秒来处理1000个键,而不是通常的
groupby
使用
read\u CSV
读取内存中完整的CSV文件而不分块的情况下,只需1秒

**********更新***********


应用Jeff的散列方法后,我可以在1秒内处理1000个键(与完整内存方法的时间相同),并且绝对减少了RAM的使用。当然,我以前没有经历过的唯一时间损失是进行分块、保存100个散列组以及从存储中的散列组中获取真实组所花费的时间。但是这个操作只需要几分钟。

这里有一个解决方案,可以任意扩展这个问题。这实际上是这个问题的高密度版本

定义一个函数,将特定的组值散列到数量较少的组中。我将这样设计,它将您的数据集划分为内存中可管理的部分

def sub_group_hash(x):
    # x is a dataframe with the 'user id' field given above
    # return the last 2 characters of the input
    # if these are number like, then you will be sub-grouping into 100 sub-groups
    return x['user id'].str[-2:]
使用上面提供的数据,这将在输入数据上创建一个分组帧,如下所示:

In [199]: [ (grp, grouped) for grp, grouped in df.groupby(sub_group_hash) ][0][1]
Out[199]: 
                             user id  timestamp  category
0  20140512081646222000004-927168801   20140722         7
3  20140512081646222000004-927168801   20140724         1
grp
作为组的名称,并将
grouped
作为结果帧

# read in the input in a chunked way
clean_input_reader = read_csv('input.csv', chunksize=500000)
with get_store('output.h5') as store:
    for chunk in clean_input_reader:

        # create a grouper for each chunk using the sub_group_hash
        g = chunk.groupby(sub_group_hash)

        # append each of the subgroups to a separate group in the resulting hdf file
        # this will be a loop around the sub_groups (100 max in this case)
        for grp, grouped in g:

            store.append('group_%s' % grp, grouped,
                         data_columns=['user_id','timestamp','category_clicked'],
                         min_itemsize=15)
现在,您有了一个包含100个子组的hdf文件(如果没有表示所有组,则可能会更少),每个子组都包含执行操作所需的所有数据

with get_store('output.h5') as store:

    # all of the groups are now the keys of the store
    for grp in store.keys():

        # this is a complete group that will fit in memory
        grouped = store.select(grp)

        # perform the operation on grouped and write the new output
        grouped.groupby(......).apply(your_cool_function)
因此,在这种情况下,这将使问题减少100倍。如果这还不够,那么只需增加sub_group_散列即可创建更多组

由于HDF5工作得更好,您应该争取更小的数字(例如,不要让10M子组达不到目的,100、1000、甚至10k都可以)。但我认为100应该适合你,除非你有一个非常野生的群体密度(例如,你在一个群体中有大量的数字,而在其他群体中很少)

注意,这个问题很容易扩展;如果需要,可以将子组存储在单独的文件中,和/或在必要时单独(并行)处理它们


这将使您的soln时间大约为
O(子组的数量)

为什么您不只是读取所有数据并进行常规分组?我展示的技术只有在相对较低的群体密度和大量数据的情况下才有意义。内存中的数据可能只有几个Gig(csv的大小不是内存中的大小)。你应该先试试这个。如果你不能这样做,那么你应该简单地预处理并通过分块输出groupby结果,然后再合并。我的数据不适合RAM,我已经尝试过了。你说的“如果你不能这样做,那么你应该简单地通过分块和组合来预处理和输出groupby结果”是什么意思?请你给我更多的细节?我只是不明白你在想什么?我所知道的是,我不能对所有数据进行分组,如果我一块一块地分组,我不知道以后如何组合具有相同键的行。用一个输入和输出的小副本pastable示例演示您想要的示例。show pd.show_versions()我真的很抱歉,我不能粘贴治疗,因为它不是我的财产。我只能告诉你,我必须按照userid进行分组,然后根据类别上的clikc及其时间戳为每个类别指定一个特定的权重,我真的需要知道给定类别中用户的所有单击来计算分数。这就是为什么我不能计算分组分组的部分分数,然后进行总结。我真的需要考虑一个给定类别的用户ID的全部信息来进行治疗。非常感谢您的帮助。你的回答很好。来自数学背景,我真的缺乏散列的想法等等。。。请随意问我任何数学问题,如果我不能回答,我会问朋友。@user3478208 chunk.groupby(sub_groupby_chunk)是正确的(尽管您指定的其他方式也可以);这是一个grouper函数,但您尚未介绍
with get_store('output.h5') as store:

    # all of the groups are now the keys of the store
    for grp in store.keys():

        # this is a complete group that will fit in memory
        grouped = store.select(grp)

        # perform the operation on grouped and write the new output
        grouped.groupby(......).apply(your_cool_function)