Python 如何筛选数据帧中包含列表列中特定子序列的行?
我有一个如下所示的数据帧:Python 如何筛选数据帧中包含列表列中特定子序列的行?,python,pandas,dataframe,performance,Python,Pandas,Dataframe,Performance,我有一个如下所示的数据帧: df = pd.DataFrame({"id": [1, 2, 3, 4, 5], "list": [[2, 51, 6, 8, 3], [19, 2, 11, 9], [6, 8, 3, 9, 10, 11], [4, 5], [8, 3, 9, 6]]}) 我想过滤这个数据帧,使它只包含X是list列的子序列的行(,因此X中元素的顺序与列表中元素的顺序相同,并且它们不会被列表中的其他元
df = pd.DataFrame({"id": [1, 2, 3, 4, 5],
"list": [[2, 51, 6, 8, 3], [19, 2, 11, 9], [6, 8, 3, 9, 10, 11], [4, 5], [8, 3, 9, 6]]})
我想过滤这个数据帧,使它只包含X
是list
列的子序列的行(,因此X中元素的顺序与列表中元素的顺序相同,并且它们不会被列表中的其他元素交错)
例如,如果X=[6,8,3]
,我希望输出如下所示:
id list
1 [2, 51, 6, 8, 3]
3 [6, 8, 3, 9, 10, 11]
我知道我可以通过以下功能检查列表是否是另一个列表的子序列(在上找到):
我有两个问题:
问题1:
如何将此应用于我的示例中的Pandas DataFrame列
问题2:
这是最有效的方法吗?如果不是,会是什么?这个函数看起来不那么优雅,我必须把它应用到一个大约200K行的非常大的数据框中
[注意:
列表
列中的列表元素是唯一的,如果这有助于优化内容的话]下面是该列的解决方案调用函数:
df = df[df.list.map(lambda x: x_in_y(X, x))]
#alternative
#df = df[df.list.apply(lambda x: x_in_y(X, x))]
print (df)
id list
0 1 [2, 51, 6, 8, 3]
2 3 [6, 8, 3, 9, 10, 11]
样本数据的性能非常好,在实际测试中也是最好的:
#200k rows
df = pd.concat([df] * 40000, ignore_index=True)
print (df)
X = [6, 8, 3]
x = to_string([6, 8, 3])
In [166]: %timeit df.list.map(lambda x: x_in_y(X, x))
214 ms ± 6.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [167]: %timeit df['list'].map(to_string).str.contains(x)
413 ms ± 4.41 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [168]: %timeit df["list"].apply(has_subsequence, subseq=X)
5.2 s ± 420 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [169]: %timeit df.list.apply(lambda y: ''.join(map(str,X)) in ''.join(map(str,y)))
573 ms ± 116 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
您可以尝试以下方法:
x = [6, 8, 3]
df = df.loc[df.list.apply(lambda y: ''.join(map(str,x)) in ''.join(map(str,y)))]
import pandas as pd
def to_string(l):
return '-' + '-'.join(map(str, l)) + '-'
X = to_string([6, 8, 3])
df = pd.DataFrame({"id": [1, 2, 3, 4, 5], "list": [[2, 51, 6, 8, 3], [19, 2, 11, 9], [6, 8, 3, 9, 10, 11], [4, 5], [8, 3, 9, 6]]})
df[df['list'].map(to_string).str.contains(X)]
# id list
# 0 1 [2, 51, 6, 8, 3]
# 2 3 [6, 8, 3, 9, 10, 11]
输出:
id list
0 1 [2, 51, 6, 8, 3]
2 3 [6, 8, 3, 9, 10, 11]
使用:
将numpy导入为np
def滚动窗口(a,尺寸):
a=np.数组(a)
形状=形状[:-1]+(形状[-1]-大小+1,大小)
步幅=a.步幅+(a.步幅[-1],)
返回np.lib.stride\u tricks.as\u striped(a,shape=shape,stripes=stripes)
def具有子序列(a,子序列Q):
返回(滚动窗口(a,len(subseq))==subseq.all(轴=1.any)()
mask=df[“list”]。应用(有子序列,子序列=[6,8,3])
df[遮罩]
说明:
rolling_window
以给定的形状和步幅在阵列中创建视图:
>>> rolling_window([1,2,3,4], 2)
np.array([[1,2], [2,3], [3,4]])
然后我们将结果与我们的目标X
>>> np.array([[1,2], [2,3], [3,4]]) == [2,3]
np.array([[False, False], [True, True], [False, False]])
然后,我们告诉numpy在所有项目在第一个轴上都是True
的情况下返回True
>>> np.array([[False, False], [True, True], [False, False]]).all(axis=1)
np.array([False, True, False])
最后,如果数组中存在任何True
,则返回True
>>> np.array([False, True, False]).any()
您可以尝试以下方法:
x = [6, 8, 3]
df = df.loc[df.list.apply(lambda y: ''.join(map(str,x)) in ''.join(map(str,y)))]
import pandas as pd
def to_string(l):
return '-' + '-'.join(map(str, l)) + '-'
X = to_string([6, 8, 3])
df = pd.DataFrame({"id": [1, 2, 3, 4, 5], "list": [[2, 51, 6, 8, 3], [19, 2, 11, 9], [6, 8, 3, 9, 10, 11], [4, 5], [8, 3, 9, 6]]})
df[df['list'].map(to_string).str.contains(X)]
# id list
# 0 1 [2, 51, 6, 8, 3]
# 2 3 [6, 8, 3, 9, 10, 11]
在我的建议中,在字符串的开头和结尾添加分隔符是很重要的。否则,您将在列表中遇到问题,例如:
[666,8,3]
我需要子集的顺序相同,这就是为什么我说的是“subsequence”而不是简单的“subsequence”@Peter-您是对的,答案已编辑。同时增加了计时,似乎这个解决方案是最快的。是的,考虑一下,但不确定是否更好的性能这是我想到的第一件事。对表演不太确定@耶斯雷利亚,第一个被接受的答案,我明白了。