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-您是对的,答案已编辑。同时增加了计时,似乎这个解决方案是最快的。是的,考虑一下,但不确定是否更好的性能这是我想到的第一件事。对表演不太确定@耶斯雷利亚,第一个被接受的答案,我明白了。