假设一个组有值
x_1, ..., x_n
。
整个组的平均值将是:
m = (x_1 + ... + x_n)/n
组中不包括
x_i
的总和为:
(m*n - x_i)
组内剔除
x_i
后的平均值为:
(m*n - x_i)/(n-1)
因此,您可以使用以下方法计算所需列的值:
import pandas as pd
df = pd.DataFrame([[1, 1, 0.5], [1, 1, 0.5], [1, 0, 1], [2, 1, 0.5], [2, 0, 1],
[2, 1, 0.5], [3, 1, 0.5], [3, 0, 1], [3, 1, 0.5]],
columns=['a', 'b', 'c'])
grouped = df.groupby(['a'])
n = grouped['b'].transform('count')
mean = grouped['b'].transform('mean')
df['result'] = (mean*n - df['b'])/(n-1)
产生的结果
In [32]: df
Out[32]:
a b c result
0 1 1 0.5 0.5
1 1 1 0.5 0.5
2 1 0 1.0 1.0
3 2 1 0.5 0.5
4 2 0 1.0 1.0
5 2 1 0.5 0.5
6 3 1 0.5 0.5
7 3 0 1.0 1.0
8 3 1 0.5 0.5
In [33]: assert df['result'].equals(df['c'])
根据下面的评论,在原始问题的实际用例中,DataFrame的
a
列包含字符串:
def make_random_str_array(letters, strlen, size):
return (np.random.choice(list(letters), size*strlen)
.view('|S{}'.format(strlen)))
N = 3*10**6
df = pd.DataFrame({'a':make_random_str_array(letters='ABCD', strlen=10, size=N),
'b':np.random.randint(10, size=N)})
因此,在3百万个数据中,df['a']
中大约有一百万个唯一值:
In [87]: uniq, key = np.unique(df['a'], return_inverse=True)
In [88]: len(uniq)
Out[88]: 988337
In [89]: len(df)
Out[89]: 3000000
在这种情况下,上述计算(在我的机器上)需要大约
11秒钟:
In [86]: %%timeit
....: grouped = df.groupby(['a'])
n = grouped['b'].transform('count')
mean = grouped['b'].transform('mean')
df['result'] = (mean*n - df['b'])/(n-1)
....: ....: ....: ....:
1 loops, best of 3: 10.5 s per loop
Pandas将所有字符串列值转换为object
dtype类型。但是我们可以将DataFrame列转换为具有固定宽度dtype的NumPy数组,并根据这些值进行分组。
以下是一个基准测试,如果我们将具有对象dtype类型的序列转换为具有固定宽度字符串dtype类型的NumPy数组,则计算所需时间不到2秒钟:
In [97]: %%timeit
....: grouped = df.groupby(df['a'].values.astype('|S4'))
n = grouped['b'].transform('count')
mean = grouped['b'].transform('mean')
df['result'] = (mean*n - df['b'])/(n-1)
....: ....: ....: ....:
1 loops, best of 3: 1.39 s per loop
请注意,您需要知道
df['a']
中字符串的最大长度才能选择适当的定宽数据类型。在上面的示例中,所有字符串的长度都为4,因此
|S4
适用。如果您使用
|Sn
,其中
n
是某个整数且
n
小于最长字符串,则这些字符串将被默默地截断而没有错误警告。这可能会导致将不应分组在一起的值进行分组。因此,责任在于您选择正确的定宽数据类型。
您可以使用
dtype = '|S{}'.format(df['a'].str.len().max())
grouped = df.groupby(df['a'].values.astype(dtype))
为确保转换使用正确的数据类型。
df = pd.concat([df]*1000000)
使得len(df)
为900万,%timeit df.groupby(['a'])['b'].transform(ave_others)
完成时间为1.18秒。 - unutbudf.info()
找到dtypes。 - unutbudtype=object
。 - PascalVKooten