如果列项包含NaN,则删除行



我有一个系列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

最新更新