Панды вычисляют сумму нескольких столбцов с учетом нескольких условий

У меня есть широкая таблица в следующем формате (до 10 человек):

person1_status | person2_status | person3_status | person1_type | person_2 type | person3_type 
       0       |        1       |        0       |        7     |        4      |        6  

Где статус может быть 0 или 1 (первые 3 столбца).

Где тип может быть # в диапазоне 4-7. Значение здесь соответствует другой таблице, которая указывает значение в зависимости от типа. Так...

Type | Value
 4   |   10
 5   |   20
 6   |   30
 7   |   40

Мне нужно рассчитать два столбца, "A" и "B", где:

  1. A - сумма значений типа каждого человека (в этом ряду), где status = 0.
  2. B - сумма значений типа каждого человека (в этом ряду), где статус = 1.

Например, результирующие столбцы "A" и "B" будут выглядеть следующим образом:

A  | B
70 | 10

Объяснение этого:

"A" имеет значение 70, потому что person1 и person3 имеют "status" 0 и имеют соответствующий тип 7 ​​и 6 (что соответствует значениям 30 и 40).

Точно так же должен быть другой столбец "B", который имеет значение "10", потому что только person2 имеет статус "1", а их тип - "4" (который имеет соответствующее значение 10).

Это, вероятно, глупый вопрос, но как мне сделать это векторизованным способом? Я не хочу использовать цикл for или что-то еще, так как это будет менее эффективно...

Надеюсь, это имело смысл... кто-нибудь может мне помочь? Я думаю, что я с ума с ума, пытаясь понять это.

Для более простых вычисляемых столбцов мне не нравилось просто np.where, но я немного застрял здесь, поскольку мне нужно вычислить сумму значений из нескольких столбцов при определенных условиях, извлекая эти значения из отдельной таблицы...

надеюсь, что это имело смысл

2 ответа

Решение

Используйте метод filter, который будет фильтровать имена столбцов для тех, где в них появляется строка.

Создайте фрейм данных для значений поиска other_table и установите индекс в качестве столбца типа.

df_status = df.filter(like = 'status')
df_type = df.filter(like = 'type')
df_type_lookup = df_type.applymap(lambda x: other_table.loc[x]).values

df['A'] = np.sum((df_status == 0).values * df_type_lookup, 1)
df['B'] = np.sum((df_status == 1).values * df_type_lookup, 1)

Полный пример ниже:

Создать поддельные данные

df = pd.DataFrame({'person_1_status':np.random.randint(0, 2,1000) , 
                   'person_2_status':np.random.randint(0, 2,1000), 
                   'person_3_status':np.random.randint(0, 2,1000), 
                   'person_1_type':np.random.randint(4, 8,1000), 
                   'person_2_type':np.random.randint(4, 8,1000),
                   'person_3_type':np.random.randint(4, 8,1000)},
                 columns= ['person_1_status', 'person_2_status', 'person_3_status',
                           'person_1_type', 'person_2_type', 'person_3_type'])

 person_1_status  person_2_status  person_3_status  person_1_type  \
0                1                0                0              7   
1                0                1                0              6   
2                1                0                1              7   
3                0                0                0              7   
4                0                0                1              4   

   person_3_type  person_3_type  
0              5              5  
1              7              7  
2              7              7  
3              7              7  
4              7              7 

Делать other_table

other_table = pd.Series({4:10, 5:20, 6:30, 7:40})

4    10
5    20
6    30
7    40
dtype: int64

Отфильтруйте столбцы состояния и типа в их собственные рамки данных.

df_status = df.filter(like = 'status')
df_type = df.filter(like = 'type')

Сделать таблицу поиска

df_type_lookup = df_type.applymap(lambda x: other_table.loc[x]).values

Применить матричное умножение и суммирование по строкам.

df['A'] = np.sum((df_status == 0).values * df_type_lookup, 1)
df['B'] = np.sum((df_status == 1).values * df_type_lookup, 1)

Выход

 person_1_status  person_2_status  person_3_status  person_1_type  \
0                0                0                1              7   
1                0                1                0              4   
2                0                1                1              7   
3                0                1                0              6   
4                0                0                1              5   

   person_2_type  person_3_type   A   B  
0              7              5  80  20  
1              6              4  20  30  
2              5              5  40  40  
3              6              4  40  30  
4              7              5  60  20  

Рассмотреть кадр данных df

mux = pd.MultiIndex.from_product([['status', 'type'], ['p%i' % i for i in range(1, 6)]])
data = np.concatenate([np.random.choice((0, 1), (10, 5)), np.random.rand(10, 5)], axis=1)
df = pd.DataFrame(data, columns=mux)
df

введите описание изображения здесь

Как это структурировано, мы можем сделать это для type == 1

df.status.mul(df.type).sum(1)

0    0.935290
1    1.252478
2    1.354461
3    1.399357
4    2.102277
5    1.589710
6    0.434147
7    2.553792
8    1.205599
9    1.022305
dtype: float64

и для type == 0

df.status.rsub(1).mul(df.type).sum(1)

0    1.867986
1    1.068045
2    0.653943
3    2.239459
4    0.214523
5    0.734449
6    1.291228
7    0.614539
8    0.849644
9    1.109086
dtype: float64

Вы можете получить ваши столбцы в этом формате, используя следующий код

df.columns = df.columns.str.split('_', expand=True)
df = df.swaplevel(0, 1, 1)
Другие вопросы по тегам