我有一个中等大小的文件(~300MB),其中包含个人列表(~300k)以及他们执行的操作。我正在尝试使用groupBy
和apply
的并列版本描述here为每个人应用操作。它看起来像这样
import pandas
import multiprocessing
from joblib import Parallel, delayed
df = pandas.read_csv(src)
patients_table_raw = apply_parallel(df.groupby('ID'), f)
def applyParallel(dfGrouped, func):
retLst = Parallel(n_jobs=multiprocessing.cpu_count())(delayed(func)(group) for name, group in dfGrouped)
return pd.concat(retLst)
但不幸的是,这消耗了很多空间。我认为这与简单命令的事实有关:
list_groups = list(df.groupby('ID'))
消耗几GB的内存!如何开始?我最初的想法是在小的'堆栈'中迭代groupBy,而不是消耗太多的内存(但是我没有找到一种方法这样做而不将其转换为列表)。
我有一个简单的CSV数据集,方式如下:
|-------------------------|
| ID | Timestamp | Action |
|-------------------------|
|1 | 0 | A |
|1 | 10 | B |
|1 | 20 | C |
|2 | 0 | B |
|2 | 15 | C |
...
我基本上要做的是创建一个不同的表,其中包含个人及其ID的操作序列/时间戳的描述。这将帮助我找回个人
|------------------|
| ID | Description |
|------------------|
|1 | 0A10B20C |
|2 | 0B15C |
...
为了做到这一点,并遵循Pythonic方式,我的想法基本上是在pandas DataFrame中加载第一个表,groupBy ID,并在分组中应用一个函数,该函数返回我想要的表的一行对于每个组(每个ID)。但是,我的数据集中有很多个人(大约100万),而且groupBy操作非常昂贵(没有明确的垃圾收集,正如我在自己的回答中提到的那样)。此外,并行化groupBy暗示了重要的内存使用,因为显然有些事情会重复。
因此,更详细的问题是:如何使用groupBy(因此比你实现自己的循环更快地进行数据处理)并且不会获得这么大的内存开销?
答案 0 :(得分:2)
试试这个(没有并行化):
In [87]: df
Out[87]:
ID Timestamp Action
0 1 0 A
1 1 10 B
2 1 20 C
3 2 0 B
4 2 15 C
In [88]: df.set_index('ID').astype(str).sum(axis=1).groupby(level=0).sum().to_frame('Description').reset_index()
Out[88]:
ID Description
0 1 0A10B20C
1 2 0B15C
答案 1 :(得分:0)
我发现了一些评论和解决方案:
我已经尝试dask
并没有太大的区别。我想这是因为文件不够大,无法使用辅助内存。
如果在应用于组的函数内执行垃圾收集,则内存性能会显着提高。我设法通过一个简单的gc.collect()
来实现这一目标,每次$ 10000美元的互动就会发生这种情况。类似的东西:
x['ID'].head(1).values[0] % 10000 == 0:
gc.collect()
垃圾收集实际上使我的并行版本运行。但是return pd.concat(retLst)
是另一个巨大的瓶颈,消耗了大量的内存!
我的最终解决方案是以外部方式对解决方案进行并列化:
我创建了一个函数,它将执行groupBy并在范围[X,Y]
我只是创建一个池并并行运行它们。每个进程根据其范围
保存具有不同名称的文件f = functools.partial(make_patient_tables2, src="in", dest="out")
range_of = [(0, 10000), (10000, 20000), (20000, 30000)]
with Pool(cpu_count()) as p:
ret_list = p.map(f, range_of)
最后但并非最不重要的是,我连接了所有生成的文件。
请注意,这仍然是内存密集型的,因为我们必须复制表的读取(这在 make_patient_tables2 中完成,但无论如何都会发生,因为多处理不会因此,一个更好的解决方案会包含共享资源,但是垃圾收集器+不使用concat +复制原始数据只需要2-3次就足够了!
当然不漂亮。希望它可以为其他人提供帮助。