Pandas:将函数应用于每对列

时间:2017-09-20 05:00:01

标签: python pandas

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

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

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

NB 这个问题不是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

1 个答案:

答案 0 :(得分: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)),则该参数会在广播问题上扩展一个维度。使用上面的表达式,您将得到与r 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、200, 1000)导致1M循环)。因此,像往常一样,强烈希望尝试找到一种使用现有的numpy函数来实现函数f()的方法!