Вычислить среднее из каждых 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, :]
Обратите внимание, что первое наблюдение будет отсутствовать (т.е. прокатка начинается вверху)