Как нормализовать в группах на основе значения из другого столбца - PullRequest
0 голосов
/ 03 мая 2019

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

Пример: скажем, я отслеживал, сколько чашек чая и кофе я выпиваю в течение недели:

In [17]: import random
    ...: test = pd.DataFrame({
    ...:     'drink' : ['tea'] * 5 +  ['coffee'] * 5,
    ...:     'day' : ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'] * 2,
    ...:     'cups' : [random.randrange(1, 10)  for _ in range(10)]
    ...: })
    ...: test
    ...: 
    ...: 
Out[17]: 
    drink        day  cups
0     tea     monday     1
1     tea    tuesday     3
2     tea  wednesday     1
3     tea   thursday     7
4     tea     friday     1
5  coffee     monday     8
6  coffee    tuesday     1
7  coffee  wednesday     2
8  coffee   thursday     1
9  coffee     friday     1

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

In [18]: test['day_norm'] = test.groupby('day')['cups'].transform(lambda x : x / 
    ...: x.sum())
In [19]: test
Out[19]: 
    drink        day  cups  day_norm
0     tea     monday     1  0.111111
1     tea    tuesday     3  0.750000
2     tea  wednesday     1  0.333333
3     tea   thursday     7  0.875000
4     tea     friday     1  0.500000
5  coffee     monday     8  0.888889
6  coffee    tuesday     1  0.250000
7  coffee  wednesday     2  0.666667
8  coffee   thursday     1  0.125000
9  coffee     friday     1  0.500000


Но, скажем, вместо этого я хочу посмотреть, как значения меняются за неделю наделение для каждой группы на значение для понедельника - т.е. я хочу, чтобы понедельник был 1, а затем через день - относительно этого.Мне удалось придумать два разных способа сделать это, оба из которых кажутся запутанными.

Первый: я могу написать функцию, которая фильтрует групповой фрейм данных, чтобы найти значение понедельника, а затем делит серию на него:

In [20]: def normalize(df):
    ...:     monday_cups = df[df['day'] == 'monday']['cups'].mean()
    ...:     return df['cups'] / monday_cups
    ...: 
    ...: test['normalized cups'] = test.groupby('drink').apply(normalize).reset_i
    ...: ndex(level=0, drop=True)
    ...: test
    ...: 
    ...: 
Out[20]: 
    drink        day  cups  day_norm  normalized cups
0     tea     monday     1  0.111111            1.000
1     tea    tuesday     3  0.750000            3.000
2     tea  wednesday     1  0.333333            1.000
3     tea   thursday     7  0.875000            7.000
4     tea     friday     1  0.500000            1.000
5  coffee     monday     8  0.888889            1.000
6  coffee    tuesday     1  0.250000            0.125
7  coffee  wednesday     2  0.666667            0.250
8  coffee   thursday     1  0.125000            0.125
9  coffee     friday     1  0.500000            0.125

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

Два: я могу преобразовать данные в широкоформатную таблицу:

n [14]: summary = test.drop(columns=['normalized cups']).groupby(['drink', 'day'])['cups'].mean().unstack()

In [15]: summary
Out[15]: 
day     friday  monday  thursday  tuesday  wednesday
drink                                               
coffee       8       7         7        8          4
tea          9       9         4        8          4

, и тогда деление станет более простым, но мне придется потратить немного времени, чтобы вернуть его коригинальный формат:

In [16]: summary.apply(lambda x : x / summary['monday']).stack().to_frame('norma
    ...: lized_cups').reset_index()
Out[16]: 
    drink        day  normalized_cups
0  coffee     friday         1.142857
1  coffee     monday         1.000000
2  coffee   thursday         1.000000
3  coffee    tuesday         1.142857
4  coffee  wednesday         0.571429
5     tea     friday         1.000000
6     tea     monday         1.000000
7     tea   thursday         0.444444
8     tea    tuesday         0.888889
9     tea  wednesday         0.444444

Есть ли более элегантный способ сделать это?У меня есть смутная идея отсортировать фрейм данных так, чтобы сначала был понедельник, а затем что-то делать с groupby и first, но я не могу об этом подумать!

Ответы [ 3 ]

1 голос
/ 03 мая 2019

Попробуйте:

df['normalized_cups'] = df.groupby('drink').cups.apply(lambda x: x/x.iloc[0])

это предполагает, что у вас есть monday первым в каждой группе.

1 голос
/ 03 мая 2019

Я предлагаю вам отделить сбор и хранение данных от анализа данных.Например, вы можете записать каждый напиток в стол, когда пьете его.Затем вы можете запустить анализ в любое время.

Таблица данных будет выглядеть примерно так: enter image description here

Вы также можете добавить отдельную таблицу "персона"и проведите «соревнование по подсчету кофеина» со своими друзьями.

1 голос
/ 03 мая 2019

Это то, что я буду делать

t2=test.loc[test.day=='monday',['drink','cups']].groupby('drink').cups.mean()
t2
Out[1282]:
drink
coffee    8
tea       1
Name: cups, dtype: int64
test['normalized_cups']=test.cups/t2.reindex(test.drink).values
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...