【问题标题】:Python list comparison numpy optimizationPython列表比较numpy优化
【发布时间】:2021-10-22 13:05:46
【问题描述】:

我基本上有一个包含 7 列的数据框 (df1)。这些值始终是整数。 我有另一个数据框(df2),它有 3 列。其中一列是具有 7 个整数序列的列表列表。示例:

import pandas as pd
df1 = pd.DataFrame(columns = ['A','B','C','D','E','F','G'],
         data = np.random.randint(1,5,(100,7)))

df2 = pd.DataFrame(columns = ['Name','Location','Sequence'],
         data = [['Alfred','Chicago', 
                  np.random.randint(1,5,(100,7))],
                ['Nicola','New York', 
                  np.random.randint(1,5,(100,7))]])

我现在想将 df1 中的行序列与 df2 中的“序列”列进行比较,并获得重叠百分比。在原始的 for 循环中,这看起来像这样:

df2['Overlap'] = 0.
for i in range(len(df2)):
    c = sum(el in list(df2.at[i, 'Sequence']) for el in df1.values.tolist()) 
    df2.at[i, 'Overlap'] = c/len(df1)

现在的问题是我的 df2 有 500000 行,而我的 df1 通常在 50-100 左右。这意味着该任务很容易变得非常耗时。我知道必须有一种方法可以用 numpy 优化它,但我无法弄清楚。有人可以帮我吗?

【问题讨论】:

    标签: python pandas dataframe numpy optimization


    【解决方案1】:

    您可以使用数组的直接比较并对相同的值求和。使用apply 执行df2 中的每行比较:

    df2['Sequence'].apply(lambda x: (x==df1.values).sum()/df1.size)
    

    输出:

    0    0.270000
    1    0.298571
    

    要将输出保存在原始数据框中:

    df2['Overlap'] = df2['Sequence'].apply(lambda x: (x==df1.values).sum()/df1.size)
    

    【讨论】:

    • 完美,正是我需要的!谢谢!
    • @alex22200 太好了,如果这对您有帮助,请不要犹豫,点赞/标记已解决
    • 不幸的是我还没有被允许这样做......但我有一个后续问题:如果数组数组的长度不同怎么办?假设 df1 有 (50,7) 而 df2 有 (100,7)?
    【解决方案2】:

    pandas cython 中默认使用引擎,但您也可以将引擎更改为 numba 或使用 njit 装饰器来加速。查找enhancingperf

    Numba 将 python 代码转换为优化的机器代码,pandas 与 numpy 高度集成,因此也与 numba 集成。您可以尝试使用并行、nogil、缓存、fastmath 选项来加速。这种方法shines 用于需要速度的大量输入。

    Numba 可以进行即时编译,或者第一次执行需要很少的时间进行编译,后续使用会很快

    import pandas as pd
    df1 = pd.DataFrame(columns = ['A','B','C','D','E','F','G'],
             data = np.random.randint(1,5,(100,7)))
    
    df2 = pd.DataFrame(columns = ['Name','Location','Sequence'],
             data = [['Alfred','Chicago', 
                      np.random.randint(1,5,(100,7))],
                    ['Nicola','New York', 
                      np.random.randint(1,5,(100,7))]])
    
    a = df1.values
    # Also possible to add `parallel=True`
    f = nb.njit(lambda x: (x == a).mean())
    
    # This is just illustration, not correct logic. Change the logic according to needs
    # nb.njit((nb.int64,))
    # def f(x):
    #     sum = 0
    #     for i in nb.prange(x.shape[0]):
    #         for j in range(a.shape[0]):
    #             sum += (x[i] == a[j]).sum()
    #     return sum
    
    
    # Experiment with engine
    print(df2['Sequence'].apply(f))
    

    【讨论】:

    • 感谢您的回复,非常有帮助!如果数组数组的长度不同怎么办?假设 df1 有 (50,7) 而 df2 有 (100,7)?
    • 您可以使用普通 for 循环编写函数并在该函数上使用 numba jit 装饰器,它们不会降低速度,因为 numba 在后台也有高效的 SIMD 矢量化器
    • 感谢您的回复。你会这么好心并提供一个例子吗?谢谢!
    • 看这个问题stackoverflow.com/questions/68591676/…,它使用普通的for循环,也看答案,有两个for循环和numba装饰器。按照同样的方法就行了
    • @alex22200 我在 cmets 中添加了一个示例逻辑不正确只是说明,您可以根据需要进行更改。
    猜你喜欢
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 2019-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多