Pandas Groupby 使内核在 Jupyter notebook/Python 中死亡

数据挖掘 Python 熊猫 朱庇特 蟒蛇
2021-09-29 18:56:24

我在 jupyter-notebook 中有一个 groupby 需要很长时间才能运行,运行 10 分钟后它说'内核死了......',

groupby 看起来像这样:

df1.groupby(['date', 'unit', 'company', 'city'])['col1',
'col2',
'col3',
'col4',
  ...
'col20'].mean()

所有 'col' 列都是浮点值。我在本地运行一切。有任何想法吗?

更新:

df1 的形状为:

(1360, 24)

内存和数据类型:

dtypes: category(3), datetime64[ns](2), float64(17), int64(2)
memory usage: 266.9 KB

城市、日期、公司、单位的唯一大小:

len(df1.date.unique()) = 789
len(df1.unit.unique()) = 76
len(df1.company.unique()) = 205
len(df1.city.unique()) = 237

我在 MacBook Pro 上有 16GB 的内存。

更新 2:

仅当我将 groupby 列中的日期和单位作为唯一的 2 列时,它才有效。如果我添加一个公司或城市,它就不再起作用了,它会无限期地运行。

3个回答

我认为这可能是因为列中使用了不同的类型,但我在下面创建了一个示例,它适用于混合列类型。唯一真正不同的是大小 - 这就是为什么我认为你可能内存不足。

工作示例

我使用int,strdatetime对象:

In [1]: import pandas as pd                                                                                                                                                                                                                          

In [2]: import datetime                                                                                                                                                                                                                              

In [3]: df = pd.DataFrame({'Branch': 'A A A A A A A B'.split(),
                           'Buyer': 'Carl Mark Carl Carl Joe Joe Joe Carl'.split(),
                           'Quantity': [1, 3, 5, 1, 8, 1, 9, 3],
                           'Date':[datetime.datetime(2013, 1, 1, 13, 0),
                                   datetime.datetime(2013, 1, 1, 13, 5),
                                   datetime.datetime(2013, 10, 1, 20, 0),
                                   datetime.datetime(2013, 10, 2, 10, 0), 
                                   datetime.datetime(2013, 10, 1, 20, 0),
                                   datetime.datetime(2013, 10, 2, 10, 0),
                                   datetime.datetime(2013, 12, 2, 12, 0),
                                   datetime.datetime(2013, 12, 2, 14, 0)]})                                                                                                                                                                                                                                          

In [4]: df                                                                                                                                                                                                                                           
Out[4]: 
  Branch Buyer  Quantity                Date
0      A  Carl         1 2013-01-01 13:00:00
1      A  Mark         3 2013-01-01 13:05:00
2      A  Carl         5 2013-10-01 20:00:00
3      A  Carl         1 2013-10-02 10:00:00
4      A   Joe         8 2013-10-01 20:00:00
5      A   Joe         1 2013-10-02 10:00:00
6      A   Joe         9 2013-12-02 12:00:00
7      B  Carl         3 2013-12-02 14:00:00


In [5]: df.shape                                                                                                                                                                                                                                    
Out[5]: (8, 4)

现在我只是再次重复数据框,但为每个日期时间值添加一小时,只是为了增加预期的 groupby 组合的数量:

In [14]: df.iloc[0:8, 3] += datetime.timedelta(hours=1)                                                                                                                                                                                              

现在对所有列执行分组,并且只求和Quantity(这是我唯一的数字列)。

结果符合预期:

In [16]: df.groupby(["Branch", "Buyer", "Quantity", "Date"])["Quantity"].sum()                                                                                                                                                                       
Out[16]: 
Branch  Buyer  Quantity  Date               
A       Carl   1         2013-01-01 13:00:00    1
                         2013-01-01 14:00:00    1
                         2013-10-02 10:00:00    1
                         2013-10-02 11:00:00    1
               5         2013-10-01 20:00:00    5
                         2013-10-01 21:00:00    5
        Joe    1         2013-10-02 10:00:00    1
                         2013-10-02 11:00:00    1
               8         2013-10-01 20:00:00    8
                         2013-10-01 21:00:00    8
               9         2013-12-02 12:00:00    9
                         2013-12-02 13:00:00    9
        Mark   3         2013-01-01 13:05:00    3
                         2013-01-01 14:05:00    3
B       Carl   3         2013-12-02 14:00:00    3
                         2013-12-02 15:00:00    3
Name: Quantity, dtype: int64

分解你的问题

可能很难分解您的问题,因为您需要对 groupby 操作的整个数据。但是,您可以将每个组保存到磁盘,mean()分别对它们执行计算并自己合并结果。每个组的名称实际上是groupby所选列的组合。这可用于构建重用数据帧的索引。

它可能看起来像这样:

for name, group in df1.groupby(['date', 'unit', 'company', 'city']):
    print("Processing groupby combination: ", name)    # This is the current groupby combination
    result = group.mean()
    _df = pd.DataFrame(index=[name], data=[result])
    _df.to_csv("path/somewhere/" + name + ".csv

然后,您将拥有一个文件夹,其中包含每个组的结果,您只需将它们读回并合并它们即可。

其他方法

众所周知,Pandas 不能非常有效地处理大量数据集上的许多操作(与例如data.table包相比)。Dask包,它本质上是以分布式方式执行 Pandas 的事情,但这可能有点矫枉过正(你当然需要更多的资源!)

我敢打赌,公司单位是类别类型?

我无法解释根本原因,但groupby不喜欢类别类型。

将列类型更改为“对象”,它将在几毫秒内运行而不会消耗任何内存

len(df1.date.unique()) = 789
len(df1.unit.unique()) = 76
len(df1.company.unique()) = 205
len(df1.city.unique()) = 237

这给出了 2,913,350,940 种可能的组合,但您说您的数据框只有 1360 行。

尝试创建一个通过连接四列构建的列,然后进行分组。