假设我有一些数据生成如下:
N = 20
m = 3
data = np.random.normal(size=(N,m)) + np.random.normal(size=(N,m))**3
Run Code Online (Sandbox Code Playgroud)
然后我创建一些分类变量:
indx = np.random.randint(0,3,size=N).astype(np.int32)
Run Code Online (Sandbox Code Playgroud)
并生成一个DataFrame:
import pandas as pd
df = pd.DataFrame(np.hstack((data, indx[:,None])),
columns=['a%s' % k for k in range(m)] + [ 'indx'])
Run Code Online (Sandbox Code Playgroud)
我可以得到每组的平均值:
df.groubpy('indx').mean()
Run Code Online (Sandbox Code Playgroud)
我不确定如何做的是然后减去原始数据中每列的每个组的平均值,以便每个列中的数据通过组内的平均值进行标准化.任何建议,将不胜感激.
Tom*_*ger 34
In [10]: df.groupby('indx').transform(lambda x: (x - x.mean()) / x.std())
Run Code Online (Sandbox Code Playgroud)
应该这样做.
如果数据包含许多组(数千个或更多),则可接受的答案可能需要很长时间才能计算出来。
尽管groupby.transform本身是快速的,因为是在lambda函数已经矢量调用(.mean(),.std()和减法),在调用纯Python功能为每个组创建了一个相当大的开销。
可以通过使用纯矢量化的Pandas / Numpy调用而不编写任何Python方法来避免这种情况,如ErnestScribbler's answer中所示。
通过利用以下广播功能,我们可以避免合并和命名列的麻烦.transform:
def normalize_by_group(df, by):
groups = df.groupby(by)
# computes group-wise mean/std,
# then auto broadcasts to size of group chunk
mean = groups.transform(np.mean)
std = groups.transform(np.std)
return (df[mean.columns] - mean) / std
Run Code Online (Sandbox Code Playgroud)
为了进行基准测试,我将原始问题的数据生成更改为允许更多组:
def gen_data(N, num_groups):
m = 3
data = np.random.normal(size=(N,m)) + np.random.normal(size=(N,m))**3
indx = np.random.randint(0,num_groups,size=N).astype(np.int32)
df = pd.DataFrame(np.hstack((data, indx[:,None])),
columns=['a%s' % k for k in range(m)] + [ 'indx'])
return df
Run Code Online (Sandbox Code Playgroud)
只有两个组(因此只有两个Python函数调用),lambda版本仅比numpy代码慢1.8倍:
In: df2g = gen_data(10000, 2) # 3 cols, 10000 rows, 2 groups
In: %timeit normalize_by_group(df2g, "indx")
6.61 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In: %timeit df2g.groupby('indx').transform(lambda x: (x - x.mean()) / x.std())
12.3 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
将组数增加到1000,并且运行时问题变得明显。Lambda版本比numpy代码慢370倍:
In: df1000g = gen_data(10000, 1000) # 3 cols, 10000 rows, 1000 groups
In: %timeit normalize_by_group(df1000g, "indx")
7.5 ms ± 87.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In: %timeit df1000g.groupby('indx').transform(lambda x: (x - x.mean()) / x.std())
2.78 s ± 13.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
14717 次 |
| 最近记录: |