Pandas aggregation of the mean excluding the current row

How to aggregate to get the average value bfor a group a, except for the current row (the target result is in c)?

a b   c

1 1   0.5   # (avg of 0 & 1, excluding 1)
1 1   0.5   # (avg of 0 & 1, excluding 1)
1 0   1     # (avg of 1 & 1, excluding 0)

2 1   0.5   # (avg of 0 & 1, excluding 1)
2 0   1     # (avg of 1 & 1, excluding 0)
2 1   0.5   # (avg of 0 & 1, excluding 1)

3 1   0.5   # (avg of 0 & 1, excluding 1)
3 0   1     # (avg of 1 & 1, excluding 0)
3 1   0.5   # (avg of 0 & 1, excluding 1)

Data dump:

import pandas as pd
data = 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'])
+4
source share
2 answers

Suppose the group matters x_1, ..., x_n.

The average value for the whole group would be

m = (x_1 + ... + x_n)/n

The amount of the group x_iwill not be

(m*n - x_i)

The average value of the group without x_iit would be

(m*n - x_i)/(n-1)

Therefore, you can calculate the desired column of values ​​with

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)

what gives

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'])

In the comments below, in the actual use case of the OP, the DataFrame column a contains the rows:

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)})

df['a'] 3 :

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 NumPy .

, , dtype NumPy dtype , 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'], dtyty . 4, |S4 . |Sn n, n , . , . , dtype .

dtype = '|S{}'.format(df['a'].str.len().max())
grouped = df.groupby(df['a'].values.astype(dtype))

dtype.

+4

, :

# Set up input
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'])
df
   a  b    c
0  1  1  0.5
1  1  1  0.5
2  1  0  1.0
3  2  1  0.5
4  2  0  1.0
5  2  1  0.5
6  3  1  0.5
7  3  0  1.0
8  3  1  0.5

# Perform grouping, excluding the current row
results = []
grouped = df.groupby(['a'])
for key, group in grouped:
    for idx, row in group.iterrows():
        # The group excluding current row
        group_other = group.drop(idx)  
        avg = group_other['b'].mean()
        results.append(row.tolist() + [avg])

# Compare our results with what is expected
results_df = pd.DataFrame(
    results, columns=['a', 'b', 'c', 'c_new']
)
results_df
   a  b    c  c_new
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

, , .

+1

All Articles