Как я могу суммировать только первый элемент каждой группы в первом столбце GroupBy при наличии нескольких столбцов GroupBy? [Дубликат]

Вы хотите, чтобы первый >, которому не предшествовал /. Посмотрите здесь , чтобы узнать, как это сделать.

Однако наивная реализация этого приведет к сопоставлению в этом примере документа


Можете ли вы предоставить немного больше информации о проблема, которую вы пытаетесь решить? Вы выполняете итерацию через теги программно?

71
задан Max Ghenis 25 January 2017 в 19:26
поделиться

10 ответов

Ответ Paul H прав, что вам придется сделать второй объект groupby, но вы можете рассчитать процент более простым способом - просто groupby state_office и разделите столбец sales по его сумме. Копирование начала ответа Пола Х:

# From Paul H
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
                   'office_id': list(range(1, 7)) * 2,
                   'sales': [np.random.randint(100000, 999999)
                             for _ in range(12)]})
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
# Change: groupby state_office and divide by sum
state_pcts = state_office.groupby(level=0).apply(lambda x:
                                                 100 * x / float(x.sum()))

Возвраты:

                     sales
state office_id           
AZ    2          16.981365
      4          19.250033
      6          63.768601
CA    1          19.331879
      3          33.858747
      5          46.809373
CO    1          36.851857
      3          19.874290
      5          43.273852
WA    2          34.707233
      4          35.511259
      6          29.781508
105
ответ дан Community 16 August 2018 в 03:36
поделиться
  • 1
    га. о, да. путь разумнее. +1 – Paul H 30 April 2014 в 01:14
  • 2
    Что тут происходит? Как я понимаю, x является таблицей некоторого типа, поэтому 100 * x не интуитивно не имеет смысла (особенно, когда некоторые из ячеек содержат строки, такие как AZ, ...). – dhardy 6 February 2015 в 10:42
  • 3
    @dhardy state_office - это серия с мультииндексированным, поэтому это всего лишь один столбец, значения которого являются числовыми. После того, как вы выполните groupby, каждый x является подмножеством этого столбца. Имеет ли это смысл? – exp1orer 8 February 2015 в 16:22
  • 4
    Возможно, но это не сработало для меня. У панд в Python 3 работают немного по-другому? – dhardy 9 February 2015 в 10:59
  • 5
    @Veenit означает, что вы группируете первый уровень индекса, а не один из столбцов. – exp1orer 23 November 2016 в 08:50

Для краткости я бы использовал SeriesGroupBy:

In [11]: c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")

In [12]: c
Out[12]:
state  office_id
AZ     2            925105
       4            592852
       6            362198
CA     1            819164
       3            743055
       5            292885
CO     1            525994
       3            338378
       5            490335
WA     2            623380
       4            441560
       6            451428
Name: count, dtype: int64

In [13]: c / c.groupby(level=0).sum()
Out[13]:
state  office_id
AZ     2            0.492037
       4            0.315321
       6            0.192643
CA     1            0.441573
       3            0.400546
       5            0.157881
CO     1            0.388271
       3            0.249779
       5            0.361949
WA     2            0.411101
       4            0.291196
       6            0.297703
Name: count, dtype: float64

Для нескольких групп вам нужно использовать преобразование (используя Radical df ):

In [21]: c =  df.groupby(["Group 1","Group 2","Final Group"])["Numbers I want as percents"].sum().rename("count")

In [22]: c / c.groupby(level=[0, 1]).transform("sum")
Out[22]:
Group 1  Group 2  Final Group
AAHQ     BOSC     OWON           0.331006
                  TLAM           0.668994
         MQVF     BWSI           0.288961
                  FXZM           0.711039
         ODWV     NFCH           0.262395
...
Name: count, dtype: float64

Это кажется немного более результативным, чем другие ответы (чуть меньше, чем в два раза быстрее ответа Radical, для меня ~ 0.08s).

6
ответ дан Andy Hayden 16 August 2018 в 03:36
поделиться
  • 1
    Это очень быстро. Я бы рекомендовал это в качестве предпочтительного подхода панд. Действительно использует преимущества векторизации numpy и индексации панд. – Charles 23 March 2018 в 13:14
  • 2
    Это тоже сработало для меня, поскольку я работаю с несколькими группами. Благодарю. – irene 14 August 2018 в 06:01

Вы можете sum целое DataFrame и делить на state total:

# Copying setup from Paul H answer
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
# Add a column with the sales divided by state total sales.
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']

df

Возвращает

    office_id   sales state  sales_ratio
0           1  405711    CA     0.193319
1           2  535829    WA     0.347072
2           3  217952    CO     0.198743
3           4  252315    AZ     0.192500
4           5  982371    CA     0.468094
5           6  459783    WA     0.297815
6           1  404137    CO     0.368519
7           2  222579    AZ     0.169814
8           3  710581    CA     0.338587
9           4  548242    WA     0.355113
10          5  474564    CO     0.432739
11          6  835831    AZ     0.637686

Но обратите внимание, что это работает только потому, что все столбцы, отличные от state, являются числовыми, что позволяет суммировать весь DataFrame. Например, если office_id является символом вместо этого, вы получаете сообщение об ошибке:

df.office_id = df.office_id.astype(str)
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']

TypeError: неподдерживаемый тип операндов для /: 'str' и 'str'

3
ответ дан Max Ghenis 16 August 2018 в 03:36
поделиться
  • 1
    Я отредактировал, чтобы отметить, что это работает только тогда, когда все столбцы, кроме столбца groupby, являются числовыми. Но в остальном это довольно элегантно. Есть ли способ заставить его работать с другими столбцами str? – Max Ghenis 25 January 2017 в 20:18
  • 2

Вам нужно сделать второй объект groupby, который группируется по состояниям, а затем использовать метод div:

import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})

state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100


                     sales
state office_id           
AZ    2          16.981365
      4          19.250033
      6          63.768601
CA    1          19.331879
      3          33.858747
      5          46.809373
CO    1          36.851857
      3          19.874290
      5          43.273852
WA    2          34.707233
      4          35.511259
      6          29.781508

level='state' kwarg в div сообщает pandas для трансляции / присоедините базу данных к значениям на уровне state индекса.

28
ответ дан Paul H 16 August 2018 в 03:36
поделиться
  • 1
    Этот метод работает, если у вас есть 3 индекса? Сначала я сделал группу по 3 столбцам. Затем я сделал вторую группу только на 2 и вычислил сумму. Затем я пытаюсь использовать div, но с level=["index1", "index2"], но он говорит мне, что Join on level between two MultiIndex objects is ambiguous. – Ger 4 January 2017 в 14:23
  • 2
    @Ger Это действительно работает, но я не могу объяснить, что вы делаете неправильно из этого описания. Искать по сайту немного больше. Если вы ничего не нашли, создайте новый вопрос с воспроизводимым примером, демонстрирующим проблему. [Д0] stackoverflow.com/questions/20109391/… – Paul H 4 January 2017 в 16:20

Я знаю, что это старый вопрос, но ответ exp1orer очень медленный для наборов данных с большим количеством уникальных групп (вероятно, из-за лямбда). Я построил их ответ, чтобы превратить его в массивные вычисления, так что теперь это супер быстро! Ниже приведен пример кода:

Создайте тестовый dataframe с 50 000 уникальных групп

import random
import string
import pandas as pd
import numpy as np
np.random.seed(0)

# This is the total number of groups to be created
NumberOfGroups = 50000

# Create a lot of groups (random strings of 4 letters)
Group1     = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/10)]*10
Group2     = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/2)]*2
FinalGroup = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups)]

# Make the numbers
NumbersForPercents = [np.random.randint(100, 999) for _ in range(NumberOfGroups)]

# Make the dataframe
df = pd.DataFrame({'Group 1': Group1,
                   'Group 2': Group2,
                   'Final Group': FinalGroup,
                   'Numbers I want as percents': NumbersForPercents})

При группировке это выглядит так:

                             Numbers I want as percents
Group 1 Group 2 Final Group                            
AAAH    AQYR    RMCH                                847
                XDCL                                182
        DQGO    ALVF                                132
                AVPH                                894
        OVGH    NVOO                                650
                VKQP                                857
        VNLY    HYFW                                884
                MOYH                                469
        XOOC    GIDS                                168
                HTOY                                544
AACE    HNXU    RAXK                                243
                YZNK                                750
        NOYI    NYGC                                399
                ZYCI                                614
        QKGK    CRLF                                520
                UXNA                                970
        TXAR    MLNB                                356
                NMFJ                                904
        VQYG    NPON                                504
                QPKQ                                948
...
[50000 rows x 1 columns]

Массивный метод процент поиска:

# Initial grouping (basically a sorted version of df)
PreGroupby_df = df.groupby(["Group 1","Group 2","Final Group"]).agg({'Numbers I want as percents': 'sum'}).reset_index()
# Get the sum of values for the "final group", append "_Sum" to it's column name, and change it into a dataframe (.reset_index)
SumGroup_df = df.groupby(["Group 1","Group 2"]).agg({'Numbers I want as percents': 'sum'}).add_suffix('_Sum').reset_index()
# Merge the two dataframes
Percents_df = pd.merge(PreGroupby_df, SumGroup_df)
# Divide the two columns
Percents_df["Percent of Final Group"] = Percents_df["Numbers I want as percents"] / Percents_df["Numbers I want as percents_Sum"] * 100
# Drop the extra _Sum column
Percents_df.drop(["Numbers I want as percents_Sum"], inplace=True, axis=1)

Этот метод занимает около ~ 0.15 секунд

Метод верхнего ответа (с использованием лямбда-функции):

state_office = df.groupby(['Group 1','Group 2','Final Group']).agg({'Numbers I want as percents': 'sum'})
state_pcts = state_office.groupby(level=['Group 1','Group 2']).apply(lambda x: 100 * x / float(x.sum()))

Этот метод принимает ~ 21 секунда для получения того же результата.

Результат:

      Group 1 Group 2 Final Group  Numbers I want as percents  Percent of Final Group
0        AAAH    AQYR        RMCH                         847               82.312925
1        AAAH    AQYR        XDCL                         182               17.687075
2        AAAH    DQGO        ALVF                         132               12.865497
3        AAAH    DQGO        AVPH                         894               87.134503
4        AAAH    OVGH        NVOO                         650               43.132050
5        AAAH    OVGH        VKQP                         857               56.867950
6        AAAH    VNLY        HYFW                         884               65.336290
7        AAAH    VNLY        MOYH                         469               34.663710
8        AAAH    XOOC        GIDS                         168               23.595506
9        AAAH    XOOC        HTOY                         544               76.404494
4
ответ дан Radical Edward 16 August 2018 в 03:36
поделиться

Простым способом, который я использовал, является слияние после того, как 2 groupby выполнили простое деление.

import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})

state_office = df.groupby(['state', 'office_id'])['sales'].sum().reset_index()
state = df.groupby(['state'])['sales'].sum().reset_index()
state_office = state_office.merge(state, left_on='state', right_on ='state', how = 'left')
state_office['sales_ratio'] = 100*(state_office['sales_x']/state_office['sales_y'])

   state  office_id  sales_x  sales_y  sales_ratio
0     AZ          2   222579  1310725    16.981365
1     AZ          4   252315  1310725    19.250033
2     AZ          6   835831  1310725    63.768601
3     CA          1   405711  2098663    19.331879
4     CA          3   710581  2098663    33.858747
5     CA          5   982371  2098663    46.809373
6     CO          1   404137  1096653    36.851857
7     CO          3   217952  1096653    19.874290
8     CO          5   474564  1096653    43.273852
9     WA          2   535829  1543854    34.707233
10    WA          4   548242  1543854    35.511259
11    WA          6   459783  1543854    29.781508
1
ответ дан upliftedLemur 16 August 2018 в 03:36
поделиться
0
ответ дан ajkn1992 29 October 2018 в 09:09
поделиться
1
ответ дан exp1orer 29 October 2018 в 09:09
поделиться
2
ответ дан louisD 29 October 2018 в 09:09
поделиться
1
ответ дан Matthias 29 October 2018 в 09:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: