Убедитесь, что столбцы отсортированы, и используйте .values
для выравнивания операций с учетом ваших соглашений об именах.Можно использовать .where
, чтобы заполнить все.Если вы хотите повысить безопасность в случае отсутствия столбцов (т. Е. У вас есть Pac.31, но нет Pdc.31), сопоставьте имена столбцов для операций, чтобы обеспечить выравнивание.
import pandas as pd
#df = df.sort_index(axis=1)
pac = df.filter(like='Pac')
pdc = df.filter(like='Pdc')
df_res = pd.concat([pac.where(pac.notnull(), pdc.multiply(pac.mean().div(pdc.mean().values).values).values),
pdc.where(pdc.notnull(), pac.multiply(pdc.mean().div(pac.mean().values).values).values)
], axis=1)
Выход df_res
:
Pac Pac.1 Pac.2 Pdc Pdc.1 Pdc.2
0 1.000000 6.0 3.000000 1.285714 4.952381 2.0
1 1.555556 1.0 2.000000 2.000000 2.000000 1.0
2 7.000000 6.0 3.714286 7.000000 4.952381 3.0
3 6.000000 7.0 5.000000 5.000000 5.000000 7.0
4 5.000000 2.0 3.714286 6.000000 1.650794 3.0
5 2.000000 7.0 4.000000 7.000000 5.000000 1.0
6 3.000000 4.0 3.000000 4.000000 1.000000 1.0
7 1.000000 5.0 3.000000 1.285714 7.000000 3.0
8 5.000000 5.0 6.000000 4.000000 5.000000 6.0
9 5.000000 2.0 3.714286 6.428571 1.000000 3.0
Образцы данных
import numpy as np
df = pd.DataFrame(np.random.choice([1,2,3,4,5,6,7, np.NaN], (10,6)),
columns = ['Pdc', 'Pdc.1', 'Pdc.2', 'Pac', 'Pac.1', 'Pac.2'])
Pdc Pdc.1 Pdc.2 Pac Pac.1 Pac.2
0 NaN NaN 2.0 1.0 6.0 3.0
1 2.0 2.0 1.0 NaN 1.0 2.0
2 7.0 NaN 3.0 7.0 6.0 NaN
3 5.0 5.0 7.0 6.0 7.0 5.0
4 6.0 NaN 3.0 5.0 2.0 NaN
5 7.0 5.0 1.0 2.0 7.0 4.0
6 4.0 1.0 1.0 3.0 4.0 3.0
7 NaN 7.0 3.0 1.0 5.0 3.0
8 4.0 5.0 6.0 5.0 5.0 6.0
9 NaN 1.0 3.0 5.0 2.0 NaN
Объяснение:
Первый шаг - сортировка столбцов, а затем фильтрация для поиска начинающихся столбцов.со строкой 'Pac'
или 'Pdc'
.Поскольку мы отсортировали индекс, это гарантирует, что порядок упорядочен (при условии, что набор суффиксов в группах идентичен)
df = df.sort_index(axis=1)
pac = df.filter(like='Pac')
pdc = df.filter(like='Pdc')
print(pac.head(3))
# Pac Pac.1 Pac.2
#0 1.0 6.0 3.0
#1 NaN 1.0 2.0
#2 7.0 6.0 NaN
print(pdc.head(3))
# Pdc Pdc.1 Pdc.2
#0 NaN NaN 2.0
#1 2.0 2.0 1.0
#2 7.0 NaN 3.0
Теперь мы можем сделать математику.Не обращайте внимания на логику .fillna
и просто подумайте, как рассчитать, что бы мы заполнили для всего DataFrame
операции выравниваются по индексам ( и строки и столбцы).Вы можете видеть, что pac
и pdc
разделяют строку Index, но столбцы Index (имена столбцов) отличаются, что вызывает проблему:
pac.mean()
#Pac 3.888889
#Pac.1 4.500000
#Pac.2 3.714286
#dtype: float64
pdc.mean()
#Pdc 5.000000
#Pdc.1 3.714286
#Pdc.2 3.000000
#dtype: float64
pac.mean().div(pdc.mean())
#Pac NaN
#Pac.1 NaN
#Pac.2 NaN
#Pdc NaN
#Pdc.1 NaN
#Pdc.2 NaN
Однако, поскольку мы ранее отсортировали, мы можемОбратите внимание, что values
выровнены, поэтому вместо этого мы безопасно разделим каждый столбец, имея в виду доступ к массиву значений.Это дает среднее значение каждого столбца Pac
, деленное на среднее значение соответствующего столбца Pdc
.
pac.mean().div(pdc.mean().values)
#Pac 0.777778
#Pac.1 1.211538
#Pac.2 1.238095
#dtype: float64
Умножение имеет ту же проблему выравнивания, поэтому снова получите доступ к значениям, и теперь это дает нам DataFrame
, той же формы, что и подмножества, с тем, что мы должны заполнить, если значение равно нулю:
pdc.multiply(pac.mean().div(pdc.mean().values).values)
# Pdc Pdc.1 Pdc.2
#0 NaN NaN 2.476190
#1 1.555556 2.423077 1.238095
#...
Наконец, логика fillna
выполняется с where
, потому что у нас есть два DataFrames
:
pac.where(pac.notnull(), pdc.multiply(pac.mean().div(pdc.mean().values).values).values)
Может читаться как «Использовать значения в pac, где они не равны нулю, в противном случае использовать значения из расчета», что именно то, что мы хотим.Снова нам нужно получить доступ к .values
для «другого» (второго аргумента) where
, потому что опять имена столбцов не совпадают, но значения выровнены.
Сделайте это для каждой группыотдельно и присоединиться к ним обратно.