如何快速地将功能应用于大熊猫的每一个细胞



我的问题如下:我需要对pandas数据帧中的每个单元格应用一个操作。像这样,我有一个变量中所有数据帧的平均值和标准列:

columnWiseMeans = df.mean(axis=0)
columnWiseStd = df.std(axis=0)

然后在每个单元格中不是0,我将它们归一化:

for i in range(df.shape[0]):
for j in range(df.shape[1]):
if df.iloc[i, j] == 0:
continue
else:
df.iloc[i, j] = (df.iloc[i, j]-columnWiseMeans[j])/columnWiseStd[j]
print(f'in {i},{j} value {df.iloc[i, j]}')

在收集平均值和标准值后,按行进行同样的操作:

rowWiseMeans = df.mean(axis=1)
print(rowWiseMean[0])
rowWiseStd = df.std(axis=1)
print(rowWiseStd[0])
for i in range(df.shape[0]):
for j in range(df.shape[1]):
if df.iloc[i, j] == 0:
continue
else:
df.iloc[i, j] = (df.iloc[i, j]-rowWiseMeans[i])/rowWiseStd[i]
print(f'in {i},{j} value {df.iloc[i, j]}')

这里的问题是,这非常慢,因为我有一个[~70k,~70k]的数据帧。我尝试过更快的applymap,但我不知道如何告诉它,如果是0,跳过它,让我知道在哪个位置使用特定的均值和标准。有什么帮助吗?谢谢

首先,我看到您正在为每个元素分配元素,通常操作整个数据帧会更快。

我用numpy创建的(10k x 10k(数组在我的机器上运行了一些测试。

import pandas as pd
import numpy as np
r = np.random.rand
  • 在2.5秒内生成数据
N = 10000
dnp = np.random.randn(N, N) * r(N, 1) * r(1, N) + r(1, N) + r(N, 1);
df = pd.DataFrame(dnp);
  • 使用6.5秒的数据帧进行计算

这可能就是你想要的。对于您的数据帧,它将在大约5分钟内运行。

df = (df - df.mean(axis=0)) / df.std(axis=0)
df = (df - df.mean(axis=1)) / df.std(axis=1)
  • 使用numpy加快计算速度(3秒(

如果您的数据帧具有统一的类型(例如,所有条目都是float64(,那么您可以将其转换为numpy数组,使用numpy进行计算,然后返回到数据帧表示。

dnp = np.array(df)
dnp[:,:] = (dnp - dnp.mean(axis=0)) / dnp.std(axis=0)
dnp[:,:] = (dnp - dnp.mean(axis=1)) / dnp.std(axis=1)
df = pd.DataFrame(np);

如果您可以使用32位数字,那么您可以执行dnp = np.array(df, dtype=np.float32),它将在1.5秒内运行。

Python循环

Python是被解释的,正如你在上面的解决方案中看到的那样,你在一些Python指令中完成了一切。在您的示例中,您正在运行几个循环。我看到您甚至检查了零以避免计算一个元素,这可能没有帮助,因为python中的每个指令都有开销。请考虑以下示例。

t = 0
for i in range(N):
for j in range(N):
t += 1

它比你的开销少,而且需要10秒(对于10k x 10k矩阵(。使用N=70k运行此操作大约需要9分钟。

编辑1:回避nan

如注释中所述,某些行/列可能以nan结尾,这一定是0/0问题。当所有元素都说x[i] = c表示所有i时,这将发生,这意味着mean(x) = cstd(x) = 0,因此(x[i] - mean(x))/std(x) = (c - c) / 0 = 0 / 0

一种可能的解决方案是将这些值设置为0。这可以在没有显著额外成本的情况下实现,如下所示:

df = (df - df.mean(axis=0)) / df.std(axis=0).replace(0, 1)
df = (df - df.mean(axis=1)) / df.std(axis=1).replace(0, 1)

每当替换方法找到零时,它就会将结果设置为1,因此上面的表达式将变为(c - c) / 1 = 0

如果由于任何其他原因std产生nan,您可以使用fillna方法

df = (df - df.mean(axis=0)) / df.std(axis=0).fillna(1).replace(0, 1)
df = (df - df.mean(axis=1)) / df.std(axis=1).fillna(1).replace(0, 1)

此解决方案只是跳过标准偏差返回nan的行/列的标准化。

最新更新