Вычислить среднее из каждых x строк в таблице и создать новую таблицу

У меня длинная таблица данных (~ 200 строк по 50 столбцов), и мне нужно создать код, который может вычислять средние значения каждых двух строк и для каждого столбца в таблице, при этом конечный результат является новой таблицей от средних значений. Это, очевидно, сумасшествие в Excel! Я использую python3, и мне известны некоторые подобные вопросы: здесь, здесь и . Но ни один из них не помогает, поскольку мне нужен элегантный код для работы с несколькими столбцами и создается организованная таблица данных. Кстати, мой оригинальный datatable был импортирован с помощью pandas и определен как dataframe, но не смог найти простой способ сделать это в pandas. Помощь очень ценится.

Пример таблицы (короткая версия):

a   b   c   d
2   50  25  26
4   11  38  44
6   33  16  25
8   37  27  25
10  28  48  32
12  47  35  45
14  8   16  7
16  12  16  30
18  22  39  29
20  9   15  47

Ожидаемая средняя таблица:

a    b     c     d
3   30.5  31.5  35
7   35    21.5  25
11  37.5  41.5  38.5
15  10    16    18.5
19  15.5  27    38

Ответ 1

Вы можете создать искусственную группу, используя df.index//2 (или как @DSM указала, используя np.arange(len(df))//2 - чтобы она работала для всех индексов), а затем используйте groupby:

df.groupby(np.arange(len(df))//2).mean()
Out[13]: 
      a     b     c     d
0   3.0  30.5  31.5  35.0
1   7.0  35.0  21.5  25.0
2  11.0  37.5  41.5  38.5
3  15.0  10.0  16.0  18.5
4  19.0  15.5  27.0  38.0

Ответ 2

NumPythonic способ состоял бы в том, чтобы извлечь элементы в виде массива NumPy с помощью df.values, а затем преобразовать в массив 3D с элементами 2 вдоль axis=1 и 4 вдоль axis=2 и выполнить среднее сокращение вдоль axis=1 и, наконец, преобразуется обратно в кадр данных, например:

pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))

Как оказалось, вы можете ввести очень эффективный инструмент NumPy: np.einsum сделать это average-reduction как комбинацию sum-reduction и scaling-down, так же -

pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)

Обратите внимание, что в предлагаемых подходах предполагается, что число строк делится на 2.

Также, как noted by @DSM, чтобы сохранить имена столбцов, вам нужно добавить columns=df.columns при обращении к Dataframe, т.е. -

pd.DataFrame(...,columns=df.columns)

Пример прогона -

>>> df
    0   1   2   3
0   2  50  25  26
1   4  11  38  44
2   6  33  16  25
3   8  37  27  25
4  10  28  48  32
5  12  47  35  45
6  14   8  16   7
7  16  12  16  30
8  18  22  39  29
9  20   9  15  47
>>> pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))
    0     1     2     3
0   3  30.5  31.5  35.0
1   7  35.0  21.5  25.0
2  11  37.5  41.5  38.5
3  15  10.0  16.0  18.5
4  19  15.5  27.0  38.0
>>> pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)
    0     1     2     3
0   3  30.5  31.5  35.0
1   7  35.0  21.5  25.0
2  11  37.5  41.5  38.5
3  15  10.0  16.0  18.5
4  19  15.5  27.0  38.0

Тесты времени выполнения -

В этом разделе давайте рассмотрим все три подхода, перечисленные до сих пор, для решения проблемы производительности, в том числе @ayhan solution with groupby.

In [24]: A = np.random.randint(0,9,(200,50))

In [25]: df = pd.DataFrame(A)

In [26]: %timeit df.groupby(df.index//2).mean() # @ayhan solution
1000 loops, best of 3: 1.61 ms per loop

In [27]: %timeit pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))
1000 loops, best of 3: 317 µs per loop

In [28]: %timeit pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)
1000 loops, best of 3: 266 µs per loop

Ответ 3

df.set_index(np.arange(len(df)) // 2).mean(level=0)

Ответ 4

Вы можете подойти к этой проблеме, используя pd.rolling(), чтобы создать скользящее среднее значение, а затем просто захватить каждый второй элемент, используя iloc

df = df.rolling(2).mean() 
df = df.iloc[::2, :]

Обратите внимание, что первое наблюдение будет отсутствовать (т.е. прокатка начинается вверху)