【问题标题】:Check reference list in pandas column using numpy vectorization使用 numpy 向量化检查 pandas 列中的参考列表
【发布时间】:2020-01-27 21:09:30
【问题描述】:

我有一个参考列表

ref = ['September', 'August', 'July', 'June', 'May', 'April', 'March']

还有一个数据框

df = pd.DataFrame({'Month_List': [['July'], ['August'], ['July', 'June'], ['May', 'April', 'March']]})
df
    Month_List
0   [July]
1   [August]
2   [July, June]
3   [May, April, March]

我想检查参考列表中的哪些元素存在于每一行中,并转换为二进制列表

我可以使用apply 实现这一点

def convert_month_to_binary(ref,lst):
    s = pd.Series(ref)
    return s.isin(lst).astype(int).tolist()  

df['Binary_Month_List'] = df['Month_List'].apply(lambda x: convert_month_to_binary(ref, x))
df

    Month_List          Binary_Month_List
0   [July]              [0, 0, 1, 0, 0, 0, 0]
1   [August]            [0, 1, 0, 0, 0, 0, 0]
2   [July, June]        [0, 0, 1, 1, 0, 0, 0]
3   [May, April, March] [0, 0, 0, 0, 1, 1, 1]

但是,在大型数据集上使用 apply 非常慢,因此我希望使用 numpy 向量化。如何提高我的表现?

扩展

我想使用numpy vectorization,因为我现在需要在此列表中应用另一个函数

我正在尝试这样,但性能很慢。与apply 类似的结果

def count_one(lst):
    index = [i for i, e in enumerate(lst) if e != 0] 
    return len(index)

vfunc = np.vectorize(count_one)
df['Value'] = vfunc(df['Binary_Month_List']) 

【问题讨论】:

  • 仅供参考 - 矢量化根据具体情况进行。如果您正在寻找一个适用于所有场景的神奇功能,则没有。

标签: python pandas numpy


【解决方案1】:

我们可以使用explodeget_dummies,注意explode 在0.25 之后可用

df.Month_List.explode().str.get_dummies().sum(level=0).reindex(columns=ref, fill_value=0).values.tolist()
Out[79]: 
[[0, 0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 1, 1]]

#df['new']=df.Month_List.explode().str.get_dummies().sum(level=0).reindex(columns=ref, fill_value=0).values.tolist()

【讨论】:

  • 我收到此错误 - 'Series' object has no attribute 'explode'
  • @Hardikgupta "通知explode0.25之后可用"
  • 如果我们必须使用numpy向量化,我们该如何实现呢?因为我现在想对每个列表(串联)应用另一个函数
【解决方案2】:

在 pandas 中更好 not use lists this way,但可以使用 MultiLabelBinarizerDataFrame.reindex 添加缺失的类别,如果性能很重要,最后将值转换为 numpy 数组,然后再转换为 lists:

from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer()
df1 = pd.DataFrame(mlb.fit_transform(df['Month_List']),columns=mlb.classes_)
df['Binary_Month_List'] = df1.reindex(columns=ref, fill_value=0).values.tolist()

或者Series.str.joinSeries.str.get_dummiesreindex

df['Binary_Month_List'] = (df['Month_List'].str.join('|')
                                           .str.get_dummies()
                                           .reindex(columns=ref, fill_value=0)
                                           .values
                                           .tolist())
print (df)
            Month_List      Binary_Month_List
0               [July]  [0, 0, 1, 0, 0, 0, 0]
1             [August]  [0, 1, 0, 0, 0, 0, 0]
2         [July, June]  [0, 0, 1, 1, 0, 0, 0]
3  [May, April, March]  [0, 0, 0, 0, 1, 1, 1]

性能不同:

df = pd.concat([df] * 1000, ignore_index=True)

from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer()

In [338]: %timeit (df['Month_List'].str.join('|').str.get_dummies().reindex(columns=ref, fill_value=0).values.tolist())
31.4 ms ± 1.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [339]: %timeit pd.DataFrame(mlb.fit_transform(df['Month_List']),columns=mlb.classes_).reindex(columns=ref, fill_value=0).values.tolist()
5.57 ms ± 94.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [340]: %timeit df['Binary_Month_List2'] =df.Month_List.explode().str.get_dummies().sum(level=0).reindex(columns=ref, fill_value=0).values.tolist()
58.6 ms ± 461 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

  • 如何使用矢量化?
【解决方案3】:

这是一个使用 NumPy 工具的工具 -

def isin_lists(df_col, ref):
    a = np.concatenate(df_col)
    b = np.asarray(ref)

    sidx = b.argsort()
    c = sidx[np.searchsorted(b,a,sorter=sidx)]

    l = np.array([len(i) for i in df_col])
    r = np.repeat(np.arange(len(l)),l)

    out = np.zeros((len(l),len(b)), dtype=bool)
    out[r,c] = 1
    return out.view('i1')

给定样本的输出 -

In [79]: bin_ar = isin_lists(df['Month_List'], ref)

In [80]: bin_ar
Out[80]: 
array([[0, 0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1]], dtype=int8)

# To assign as lists for each row into `df`
In [81]: df['Binary_Month_List'] = bin_ar.tolist()

# To get counts
In [82]: df['Value'] = bin_ar.sum(1)

In [83]: df
Out[83]: 
            Month_List      Binary_Month_List  Value
0               [July]  [0, 0, 1, 0, 0, 0, 0]      1
1             [August]  [0, 1, 0, 0, 0, 0, 0]      1
2         [July, June]  [0, 0, 1, 1, 0, 0, 0]      2
3  [May, April, March]  [0, 0, 0, 0, 1, 1, 1]      3

如果由于某种原因您不能使用中间 bin_ar 并且只有 'Binary_Month_List' 标头可以使用 -

In [15]: df['Value'] = np.vstack(df['Binary_Month_List']).sum(axis=1)

【讨论】:

  • 我不能直接使用 sum,因为该函数中有更多步骤。仅对于此示例,我正在计算 1 的计数
  • @Hardikgupta 知道了。已编辑帖子。
【解决方案4】:

我不确定这是否会更快。不过这种情况下也可以使用count-vector

from sklearn.feature_extraction.text import CountVectorizer
vect=CountVectorizer(binary=True)

mys=([(','.join(i)) for i in df['Month_List']])
X=vect.fit_transform(mys)
col_names=vect.get_feature_names()
ndf=pd.SparseDataFrame(X, columns=col_names)
df=df.join(ndf).astype(str)
df['Binary_Month_List'] = df.iloc[:, 1:].values.tolist()

【讨论】:

    猜你喜欢
    • 2021-09-23
    • 2011-05-28
    • 1970-01-01
    • 2017-09-30
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    相关资源
    最近更新 更多