【问题标题】:Pandas: Apply function to each pair of columnsPandas:将函数应用于每对列
【发布时间】:2020-11-10 00:03:35
【问题描述】:

函数f(x,y) 接受两个 Pandas Series 并返回一个浮点数。我想将f 应用于DataFrame D 中的每一对列,并构造返回值的另一个DataFrame E,以便f(D[i],D[j])ith 行和j 的值第列。直接的解决方案是在所有列对上运行嵌套循环:

E = pd.DataFrame([[f(D[i], D[j]) for i in D] for j in D],
                 columns=D.columns, index=D.columns)

但是有没有更优雅的解决方案可能不涉及显式循环?

注意这个问题不是this的骗子,尽管名字相似。

编辑一个玩具示例:

D = pd.DataFrame([[1,2,3], [4,5,6], [7,8,9]], columns=("a","b","c"))
def f(x,y): return x.dot(y)

E
#    a    b    c
#a  66   78   90
#b  78   93  108
#c  90  108  126

【问题讨论】:

  • @Zero Sure,已添加。
  • pd.DataFrame({j: {i: f(D[i], D[j]) for i in D} for j in D}) 怎么样?
  • @Zero 有什么不同?它使用相同的嵌套循环。
  • 没错,它只是避免显式给出列和索引。
  • D.apply(lambda x: D.apply(lambda y: f(x, y))) 也是循环的替代品,但你觉得它比你的更优雅吗?

标签: python pandas


【解决方案1】:

您可以使用Numpy's broadcasting 来避免显式循环。

结合np.vectorize() 和显式签名,我们得到以下信息:

vf = np.vectorize(f, signature='(n),(n)->()')
result = vf(D.T.values, D.T.values[:, None])

注意事项:

  1. 您可以在您的函数中添加一些打印语句(例如print(f'x:\n{x}\ny:\n{y}\n')),以说服自己它正在做正确的事情。
  2. 你的函数f()是对称的;如果不是(例如def f(x, y): return np.linalg.norm(x - y**2)),则该参数将扩展为广播事务的额外维度。使用上面的表达式,您将得到与E 相同的结果。如果你改用result = vf(D.T.values[:, None], D.T.values),那么你会得到它的转置。
  3. 当然,结果是一个 numpy 数组,如果您希望它返回为 DataFrame,请添加:
df = pd.DataFrame(result, index=D.columns, columns=D.columns)

顺便说一句,如果f() 真的是你的玩具示例中的那个,我相信你已经知道了,你可以直接写:

df = D.T.dot(D)

性能:

在性能方面,使用广播和矢量化的加速大约是 10 倍(在各种矩阵大小上稳定)。相比之下,D.T.dot(D) 对于尺寸 (100, 100) 的速度要快 700 倍以上,但重要的是,随着尺寸的增大,相对加速似乎会更高(在我的测试中,对于尺寸 (200, 1000),相对速度提高了 12,000 倍)导致 1M 循环)。所以,像往常一样,有强烈的动机去尝试找到一种方法来使用现有的 numpy 函数来实现你的函数f()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 2011-07-11
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    相关资源
    最近更新 更多