0

我有一个非常巨大的 DataFrame,其中包含数百万行和大约 20-30 列,其中包含各种类型的数据,例如。字符串、数字、日期等

df

index           t1      num1    float1  ...          str2  
0       2014-10-21      3456     0.000  ...  ayzkcxtoScUy             
1       2014-10-21      2453     0.000  ...  jZygJWtxyVnS             
...            ...       ...       ...  ...           ...           
n-1     2020-11-06    708735   670.818  ...  UWVhmKCfmzVj             
n       2020-11-06     70630   670.817  ...  EvhreYZotqVS             

让我们说它很疯狂,但我需要每一行都有它的所有值。我现在想对某些列进行分组,并df根据组大小从原始 DataFrame 中消除组和行。特别是,我想消除所有大小为 1 的组。

第一种天真的方法

我搜索并尝试使用这个答案:如何在 Pandas 数据框中选择值出现多次的行

lst = ["t1", "str1", "num1", "str2", "num2"]
df = df.groupby(lst).filter(lambda x: len(x.index) > 1).reset_index(drop=True)

这确实按预期工作。我的 DataFramedf现在从大小为 1 的组中出现的所有行中过滤。问题是使用 filter 方法与我的 DataFrame 的尺寸相关的时间,这需要太长时间。从这个角度来看,对这些样本列进行分组将产生大约 165,000 个组和 250 万行 DataFrame,其中大约三分之一的组的大小为 1。我不得不中止这个脚本的执行,因为它需要年龄。我进一步尝试使用来自此链接的灵感如何提高熊猫 GroupBy 过滤器操作的性能?但无法使用它map,因为我在 DataFrame 上而不是在 Series 上进行分组。使用该transform方法,性能恶化

边注

进一步调查,我发现在具有和/或列filter的 DataFrame 上使用时出现了问题。我曾经删除所有这三个列,这将过滤方法的性能提高了大约三分之一。仍然不够,但足以在这里提及它,尤其是当我需要这些列并且不能只删除它们时。datetime64[ns, UTC]datetime64[ns]Del df[x]

第二种“聪明”的方法

然后,我尝试通过使用.value_counts()from link 1巧妙地对我的数据进行索引来规避 groupby、过滤或转换的使用。

vc = df[lst].value_counts()
vc_index = vc.index[vc.gt(1)]
df = data[data[lst].isin(vc_index)]

我正在获取值计数vc以定位计数为 1 的所有索引,然后创建一个new_index仅包含所需索引(即count > 1)的 MultiIndex。之后,我尝试在链接1df中使用like 过滤我,它将所有值设置为 NaN/NaT。我被困在这里 - 我不确定我在这里做错了什么。.isin()df

df

index      t1      num1    float1  ...    str2  
0         NaT       NaN       NaN  ...     NaN             
1         NaT       NaN       NaN  ...     NaN             
...       ...       ...       ...  ...     ...           
n-1       NaT       NaN       NaN  ...     NaN             
n         NaT       NaN       NaN  ...     NaN             

在另一次尝试中,我尝试使用该pd.index.difference()方法

vc = data[lst].value_counts()
df = data.set_index(keys=lst)
df.index = df.index.difference(other=vc.index[vc.gt(1)])

但这只给了我一个TypeError: '<' not supported between instances of 'float' and 'str'.

老实说,我有点不知道什么是最好的,因为两天以来一直坐在这个问题上。我什至考虑过并行化(也许使用 Dask?),但我不确定如何使用它,因为我从未使用过它。帮助将不胜感激。

4

2 回答 2

3

对于这个特定的用例(计数 > 1 的组),duplicated速度要快得多:

df[df.duplicated(lst, keep=False)]
# 231 ms ± 10.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

另一种选择,不是那么快,但比filter一般情况下要快得多groupby().transform('size')

df[df.groupby(lst)['t1'].transform('size')>1]
# 554 ms ± 108 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

相比:

df.groupby(lst).filter(lambda x: len(x) > 1)
# CPU times: user 38.8 s, sys: 482 ms, total: 39.3 s
# Wall time: 39.4 s
于 2020-12-21T22:00:42.413 回答
0

@Quang Hoang 的解决方案效果很好。我用我的数据集做的一些基准测试:

(rs = 的行df,ngrps = df.groupby(lst).ngroups

method   100k rs/82.488 ngrps  200k rs/164.466 ngrps  400k rs/331.351 ngrps  800k rs/672.905 ngrps  1.600k rs/1.351.525 ngrps

duplicated     0:00:00.031236         0:00:00.078112         0:00:00.181825         0:00:00.331095             0:00:00.683959
transform      0:00:00.062507         0:00:00.109386         0:00:00.261506         0:00:00.528166             0:00:01.029606
filter         0:00:09.039214         0:00:18.422355         0:00:37.372117         0:01:15.531945             0:02:32.075144

使用重复可以很好地扩展,但请注意:如果列表内的列中有 NaN 值(在我的示例中,您要对其进行分组lst),重复将不会删除它们

于 2020-12-22T14:06:14.330 回答