我有一个系列s
,它的条目是列表,例如[1, 2, 3, NaN, NaN]
或[4, 5]
。这些列表可能包含NaN作为最后几个元素,并且我想删除本系列中包含NaN的所有整体。到目前为止,我已经使用了s.transform(lambda x: np.nan if np.isnan(x).any() else x).dropna()
,但是这只需要2100万行就需要一分钟以上的时间,并且我最终计划对数百亿行执行此操作,所以我需要一些快速的东西。谢谢你!
要强调的是,系列中的每个条目都是一个列表,因此我不能只使用pd.dropna()
,因为没有NaN条目,因为它们本身就是列表。我想删除包含NaN的列表(条目)。s
系列可能是这样的:pd.Series([1, 2, 3, NaN, NaN], [4, 5]...)
.
您可以识别所有等于NaN
的数据帧的索引位置,然后可以过滤那些不在索引数组中的数据帧:
ser = pd.DataFrame(data={"col": [[1, 2, 3, np.nan, np.nan], [3, 4, 5], [3, 9], [np.nan, 10]]})['col']
ser_exploded = ser.explode()
ser[~ser.index.isin(np.unique(ser_exploded[ser_exploded.isna()].index))]
--------------------------------------
1 [3, 4, 5]
2 [3, 9]
Name: col, dtype: object
--------------------------------------
multiprocessing
:
import pandas as pd
import numpy as np
import multiprocessing as mp
import time
def check_nan(s):
return s.explode().isna().groupby(level=0).max()
if __name__ == '__main__': # Do not remove this line! Mandatory
# Setup a minimal reproducible example
N = 10_000_000
s = pd.Series([[1, 2, 3, np.NaN, np.NaN], [4, 5]]).repeat(N)
s = s.sample(frac=1, ignore_index=True)
CHUNKSIZE = 10_000
start = time.time()
with mp.Pool(mp.cpu_count() - 1) as p:
results = p.map(check_nan, (s[i:i+CHUNKSIZE] for i in range(0, len(s), CHUNKSIZE)))
m = pd.concat(results)
s = s[~m]
end = time.time()
print(f"Elapsed time: {end - start:.2f} seconds")
对于20,000,000条记录:
[...]$ python mp.py
Elapsed time: 1.58 seconds
注意:如果没有mp
,执行时间为6.07秒:
start = time.time()
m = s.explode().isna().groupby(level=0).max()
s1 = s[~m]
end = time.time()
print(f"Elapsed time: {end - start:.2f} seconds")
假设输入:
from numpy import NaN
s = pd.Series([[1, 2, 3, NaN, NaN], [4, 5]])
你可以使用:
s2 = s[s.explode().notna().groupby(level=0).all()]
或者,带列表推导式:
s2 = s[[pd.Series(x).notna().all() for x in s]]
输出:
1 [4, 5]
dtype: object
您将其转换为DF,爆炸,然后删除所有NA,最后将其连接回列表,作为原始系列
s.to_frame().reset_index().explode(0).dropna().groupby('index')[0].agg(list)
输入
0 [1, 2, 3, nan, nan]
1 [4, 5]
结果
0 [1, 2, 3]
1 [4, 5]
如果我正确理解了您的问题,使用测试NaN值的掩码过滤行应该可以工作
import pandas as pd
from numpy import nan as NaN
s = pd.Series([[1, 2, 3, NaN, NaN], [4, 5]])
s = s[~s.apply(lambda list1: any(pd.isna(x) for x in list1))]
print(s)
1 [4, 5]
dtype: object