Python 对另一个列表中的列表元素进行编码的最有效方法

Python 对另一个列表中的列表元素进行编码的最有效方法,python,arrays,performance,numpy,Python,Arrays,Performance,Numpy,假设我有一个排序后的主列表,如下所示: main=[“a”、“b”、“cd”、“e”] 还有一些其他的列表,比如l1=[“a”,“cd”]和l2=[“b”,“cd”]。它还保证单个列表的所有元素,如l1和l2也属于main。并非所有单个列表的长度都必须相同,但所有列表的长度都小于或等于main 现在,对于每个单独的列表,我想创建一个向量来编码main的哪些元素出现在单独的列表中。e、 g.对于l1,我们将得到一个向量(numpy数组)[1,0,1,0],因为main的第一和第三个元素(我可以说是

假设我有一个排序后的主列表,如下所示:
main=[“a”、“b”、“cd”、“e”]

还有一些其他的列表,比如
l1=[“a”,“cd”]
l2=[“b”,“cd”]
。它还保证单个列表的所有元素,如
l1
l2
也属于
main
。并非所有单个列表的长度都必须相同,但所有列表的长度都小于或等于
main

现在,对于每个单独的列表,我想创建一个向量来编码
main
的哪些元素出现在单独的列表中。e、 g.对于
l1
,我们将得到一个向量(numpy数组)
[1,0,1,0]
,因为
main
的第一和第三个元素(我可以说是“first”和“third”,因为
main
已排序)。类似地,对于
l2
我们将得到
[0,1,1,0]


做这件事的有效方法是什么?我不想采用幼稚的暴力手段,因为那会非常缓慢。是否涉及任何中间numpy转换/操作?我很乐意在这方面得到帮助

您可以使用numpy数组在列表理解中尝试if-else

import numpy as np

#encode presence of elements of a list in another list
def list_contains(mainlist, otherlist):
    result = np.array([1 if x in otherlist else 0 for x in mainlist])
    return result
你可以跟我核对一下

main = ["a", "b", "cd", "e"]
l1 = ["a", "cd"]
l2 = ["b", "cd"]

#check 1
out1 = list_contains(main, l1)
print(out1)  # [1 0 1 0]
type(out1)  # numpy.ndarray

#check 2
out2 = list_contains(main, l2)
print(out2)  # [0 1 1 0]
type(out2)  # numpy.ndarray

您可以使用列表理解:

main = ["a", "b", "cd", "e"]
l1 = ["a", "cd"]

print ([item in l1 for item in main])
输出:

[True, False, True, False]
[1, 0, 1, 0]

输出:

[True, False, True, False]
[1, 0, 1, 0]

这是一个带有
np.searchsorted
-

def isin_many(main, L):
    # main is the array or list where presence is to be detected
    # L is list of lists whose presence is to be detected
    main_ar = np.asarray(main)

    La = np.concatenate(L)
    sidx = np.argsort(main_ar)
    idx = sidx[np.searchsorted(main_ar, La, sorter=sidx)]

    n = len(L)
    out = np.zeros((n,len(main_ar)), dtype=bool)
    row = np.repeat(np.arange(n),np.array([len(l) for l in L]))
    out[row,idx] = True
    return out.view('i1')
输出将是一个
2D
数组,其中的每一行将显示主列表中每个单独列表的存在。这主要是一种矢量化方法(唯一的循环是获取单个列表的长度,这在计算上可以忽略不计),因为此类列表的数量很大

样本运行-

In [44]: main = ["a", "b", "cd", "e", "F"]
    ...: l1 = ["a", "cd"]; l2 = ["b", "cd"]; l3 = ['F']

In [45]: isin_many(main, [l1,l2,l3])
Out[45]: 
array([[1, 0, 1, 0, 0], # presence of l1
       [0, 1, 1, 0, 0], # presence of l2 and so on ...
       [0, 0, 0, 0, 1]], dtype=int8)

我的解决方案使用了numpy isin(它给出了真/假),效率方面,因为在许多情况下,它取决于大小,列表理解在小示例中更快,但在较长的情况下慢得多,如下所示:

import itertools
import string
import random 
import numpy as np

main = ["a", "b", "cd", "e"]
l1 = ["a", "cd"]

def f1(main, l):
    return [1 if x in l else 0 for x in main]

%%timeit
f1(main, l1)
output: 649 ns ± 15.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
np.isin(main, l1)
output:23 µs ± 122 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
还有一个很长的例子:

letters_and_couples = \
[''.join(x) for x in itertools.combinations(string.ascii_lowercase, 2)]+\
[''.join(x) for x in itertools.combinations(string.ascii_lowercase, 1)]

long_main = random.choices(letters_and_couples,k=1000000)
long_l1 = random.choices(letters_and_couples,k=1000)

%%timeit
f1(long_main, long_l1)
output: 5.02 s ± 71.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
np.isin(long_main, long_l1)
output: 241 ms ± 1.89 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

正如有人指出的,你可以使用
np.isin

def isin_many_np(main, L):
    return np.array([np.isin(main, l) for l in L])
但是,如果您要多次这样做,那么使用字典给出主元素的每个元素的位置会更有效

那么你只需要写以下内容:

def isin_dict(main_d, L):
    r = np.zeros(len(main_d), dtype=bool)
    for li in L:
      r[main_d[li]] = 1
    return r

def isin_many_dict(main, L):
    main_d = {s:i for i,s in enumerate(main)}
    return np.array([isin_dict(main_d, l) for l in L]).view('i1')
这里有一些关于
np.isin
和@Divakar答案的基准:

import random
import string

main = sorted([''.join(random.sample(string.ascii_letters, 5)) for i in range(4000)])
L = [random.sample(main, 2000) for i in range(6000)]

%timeit t = isin_many_np(main, L)                                                                                                                
6.67 s ± 121 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit r = isin_many_divakar(main, L)                                                                                                                      
2.17 s ± 50.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit s = isin_many_dict(main, L)                                                                                                               
1.27 s ± 11.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

它能有
[“a”、“a”、“a”]
?它的预期输出是什么?通常你会有多少个这样的列表?@Divakar:600000左右。主列表大约有4000个元素。发布的解决方案中有哪一个适合你吗?@Divakar:我使用了numpy.isin(),因为我必须将该功能包装到另一个函数中,而np.isin非常容易实现(内置一行程序),而且看起来非常有效!还有一件事——如果我在主列表和一个单独的列表中都有NaN值怎么办?我已经读到,始终记住NaN值是一种很好的做法,因此我希望忽略(为0赋值)与单个列表中NaN值对应的索引。这可能吗?@ShirishKulhari那么,这是numpy.nan还是'nan'(字符串)或者没有?