Persentase panda total dengan groupby

160

Ini jelas sederhana, tetapi sebagai pemula yang norak, saya terjebak.

Saya memiliki file CSV yang berisi 3 kolom, Negara Bagian, ID Kantor, dan Penjualan untuk kantor itu.

Saya ingin menghitung persentase penjualan per kantor di negara bagian tertentu (total semua persentase di setiap negara bagian adalah 100%).

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

df.groupby(['state', 'office_id']).agg({'sales': 'sum'})

Ini mengembalikan:

                  sales
state office_id        
AZ    2          839507
      4          373917
      6          347225
CA    1          798585
      3          890850
      5          454423
CO    1          819975
      3          202969
      5          614011
WA    2          163942
      4          369858
      6          959285

Aku tidak bisa mencari cara untuk "mencapai" ke statetingkat groupbyuntuk total sampai salesuntuk seluruh stateuntuk menghitung fraksi.

erikcw
sumber
4
df['sales'] / df.groupby('state')['sales'].transform('sum')sepertinya jawaban yang paling jelas.
Paul Rougieux

Jawaban:

217

Jawaban Paulus H benar bahwa Anda akan harus membuat kedua groupbyobjek, tetapi Anda dapat menghitung persentase dengan cara sederhana - hanya groupbyyang state_officedan membagi saleskolom dengan jumlah nya. Menyalin awal jawaban Paul H.

# 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()))

Pengembalian:

                     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
exp1orer
sumber
1
Apa yang terjadi di sini? Seperti yang saya pahami, xini adalah tabel dari beberapa jenis, jadi 100 * xtidak masuk akal secara intuitif (terutama ketika beberapa sel berisi string seperti AZ, ...).
dhardy
7
@dhardy state_officeadalah Seri dengan Multi Indeks - jadi ini hanya satu kolom yang nilainya semuanya numerik. Setelah Anda melakukan groupby, masing x- masing adalah subset dari kolom itu. Apakah itu masuk akal?
exp1orer
2
Mungkin saja, tetapi tidak berhasil untuk saya. Apakah panda di Python 3 bekerja sedikit berbeda?
dhardy
1
Apa level=0maksudnya
van_d39
3
@Veenit itu berarti Anda mengelompokkan berdasarkan level indeks pertama, bukan berdasarkan salah satu kolom.
exp1orer
55

Anda perlu membuat grup kedua dengan objek yang dikelompokkan berdasarkan negara bagian, dan kemudian menggunakan divmetode:

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

yang level='state'kwarg di divmemberitahu panda untuk siaran / bergabung dasar dataframes pada nilai-nilai di statetingkat indeks.

Paul H.
sumber
4
Apakah metode ini berfungsi jika Anda memiliki 3 indeks? Saya pertama kali melakukan groupby pada 3 kolom. Kemudian saya melakukan kelompok kedua dengan hanya 2 dan menghitung jumlahnya. Kemudian saya mencoba menggunakan divtetapi dengan level=["index1", "index2"]tetapi itu memberi tahu saya itu Join on level between two MultiIndex objects is ambiguous.
Ger
@Ger Itu berhasil, tetapi tidak mungkin saya bisa membayangkan apa yang Anda lakukan salah dari deskripsi itu. Cari lagi di sekitar situs. Jika Anda tidak menemukan apa pun, buat pertanyaan baru dengan contoh yang dapat direproduksi yang menunjukkan masalah tersebut. stackoverflow.com/questions/20109391/…
Paul H
39

Untuk ringkasnya saya akan menggunakan 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

Untuk beberapa grup Anda harus menggunakan transform (menggunakan df Radical ):

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

Ini tampaknya sedikit lebih berkinerja daripada jawaban lainnya (hanya kurang dari dua kali kecepatan jawaban Radical, bagi saya ~ 0,08s).

Andy Hayden
sumber
5
Ini sangat cepat. Saya akan merekomendasikan ini sebagai pendekatan panda yang disukai. Benar-benar memanfaatkan vektorisasi numpy dan pengindeksan panda.
Charles
Ini juga bekerja dengan baik untuk saya, karena saya bekerja dengan banyak kelompok. Terima kasih.
irene
31

Saya pikir ini perlu pembandingan. Menggunakan DataFrame asli OP,

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

1 Andy Hayden

Seperti mengomentari jawabannya, Andy memanfaatkan sepenuhnya vektorisasi dan pengindeksan panda.

c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()

3,42 ms ± 16,7 µs per loop
(rata-rata ± std. Dev. Dari 7 run, masing-masing 100 loop)


2 Paul H

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

4,66 ms ± 24,4 µs per loop
(rata-rata ± std. Dev. Dari 7 run, masing-masing 100 loop)


Exp1orer ke-3

Ini adalah jawaban paling lambat karena dihitung x.sum()untuk setiap jawaban xdi level 0.

Bagi saya, ini masih merupakan jawaban yang berguna, meski tidak dalam bentuknya yang sekarang. Untuk EDA cepat pada set data yang lebih kecil, applymemungkinkan Anda menggunakan rangkaian metode untuk menulis ini dalam satu baris. Oleh karena itu, kami menghapus kebutuhan memutuskan nama variabel, yang sebenarnya sangat mahal secara komputasi untuk sumber daya Anda yang paling berharga (otak Anda !!).

Berikut modifikasinya,

(
    df.groupby(['state', 'office_id'])
    .agg({'sales': 'sum'})
    .groupby(level=0)
    .apply(lambda x: 100 * x / float(x.sum()))
)

10.6 ms ± 81.5 µs per loop
(rata-rata ± std. Dev. Dari 7 run, masing-masing 100 loop)


Jadi tidak ada yang peduli tentang 6ms pada kumpulan data kecil. Namun, ini adalah kecepatan 3x dan, pada kumpulan data yang lebih besar dengan grup berkardinalitas tinggi, ini akan membuat perbedaan besar.

Menambah kode di atas, kami membuat DataFrame dengan bentuk (12.000.000, 3) dengan 14412 kategori negara dan 600 office_ids,

import string

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

groups = [
    ''.join(i) for i in zip(
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
                       )
]

df = pd.DataFrame({'state': groups * 400,
               'office_id': list(range(1, 601)) * 20000,
               'sales': [np.random.randint(100000, 999999)
                         for _ in range(12)] * 1000000
})

Menggunakan Andy,

2 s ± 10,4 ms per loop
(rata-rata ± std. Dev. Dari 7 run, masing-masing 1 loop)

dan exp1orer

19 s ± 77.1 ms per loop
(rata-rata ± std. Dev. Dari 7 run, masing-masing 1 loop)

Jadi sekarang kita melihat kecepatan x10 pada kumpulan data berkardinalitas tinggi yang besar.


Pastikan untuk UV ketiga jawaban ini jika Anda UV yang satu ini !!

Tabel Bobby Kecil
sumber
23

(Solusi ini terinspirasi dari artikel ini https://pbpython.com/pandas_transform.html )

Saya menemukan solusi berikut menjadi yang paling sederhana (dan mungkin tercepat) menggunakan transformation:

Transformasi: Meskipun agregasi harus mengembalikan versi data yang dikurangi, transformasi dapat mengembalikan beberapa versi data lengkap yang telah diubah untuk digabungkan kembali. Untuk transformasi seperti itu, bentuk keluarannya sama dengan masukan.

Jadi dengan menggunakan transformation, solusinya adalah 1-liner:

df['%'] = 100 * df['sales'] / df.groupby('state')['sales'].transform('sum')

Dan jika Anda mencetak:

print(df.sort_values(['state', 'office_id']).reset_index(drop=True))

   state  office_id   sales          %
0     AZ          2  195197   9.844309
1     AZ          4  877890  44.274352
2     AZ          6  909754  45.881339
3     CA          1  614752  50.415708
4     CA          3  395340  32.421767
5     CA          5  209274  17.162525
6     CO          1  549430  42.659629
7     CO          3  457514  35.522956
8     CO          5  280995  21.817415
9     WA          2  828238  35.696929
10    WA          4  719366  31.004563
11    WA          6  772590  33.298509
Caner
sumber
4
@Cancer Ini adalah jawaban favorit saya karena membuat df sebagai df (tanpa mengonversi ke seri) dan hanya menambahkan kolom%. Terima kasih
T.Fung
Variasi dari jawaban ini bekerja sangat baik untuk saya dengantransform('max')
Sheldore
11

Saya tahu ini adalah pertanyaan lama, tetapi jawaban exp1orer sangat lambat untuk kumpulan data dengan sejumlah besar grup unik (mungkin karena lambda). Saya membangun jawaban mereka untuk mengubahnya menjadi kalkulasi array jadi sekarang super cepat! Di bawah ini adalah contoh kode:

Buat kerangka data uji dengan 50.000 grup unik

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})

Saat dikelompokkan, tampilannya seperti:

                             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]

Metode array untuk menemukan persentase:

# 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)

Metode ini membutuhkan waktu sekitar ~ 0,15 detik

Metode jawaban teratas (menggunakan fungsi lambda):

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()))

Metode ini membutuhkan waktu sekitar ~ 21 detik untuk menghasilkan hasil yang sama.

Hasil:

      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
Edward yang radikal
sumber
10

Saya menyadari sudah ada jawaban yang bagus di sini.

Namun saya tetap ingin memberikan kontribusi saya sendiri, karena saya rasa untuk pertanyaan yang sederhana dan mendasar seperti ini, harus ada solusi singkat yang sekilas bisa dimengerti.

Ini juga harus bekerja dengan cara yang saya bisa menambahkan persentase sebagai kolom baru, membiarkan sisa kerangka data tidak tersentuh. Last but not least, itu harus menggeneralisasi dengan cara yang jelas untuk kasus di mana ada lebih dari satu tingkat pengelompokan (misalnya, negara bagian dan negara bukan hanya negara bagian).

Cuplikan berikut memenuhi kriteria ini:

df['sales_ratio'] = df.groupby(['state'])['sales'].transform(lambda x: x/x.sum())

Perhatikan bahwa jika Anda masih menggunakan Python 2, Anda harus mengganti x pada penyebut suku lambda dengan float (x).

MightyCurious
sumber
Ini adalah jawaban terbaik IMO. Satu-satunya hal yang perlu ditambahkan adalah * 100menjadikannya persentase.
Bouncner
1
@Bouncner: Ya, sebenarnya Anda harus mengalikan dengan 100 untuk mendapatkan persentase - atau mengganti nama variabel baru dari "sales_percentage" menjadi "sales_ratio". Secara pribadi, saya lebih suka yang terakhir, dan saya mengedit jawabannya sesuai dengan itu. Terima kasih telah menyebutkan!
MightyCurious
2
Ini tidak berfungsi jika Anda memiliki beberapa level.
irene
@irene: Poin bagus, terima kasih! Mungkin dalam kasus itu df.reset_index (). Groupby (['state']) ['sales']. Transform (lambda x: x / x.sum ()) akan bekerja. Atau apakah saya mengabaikan sesuatu?
MightyCurious
1
Jawaban ini bagus. Ini tidak melibatkan pembuatan groupbyobjek sementara , sangat ringkas, dan membaca dengan sangat logis dari kiri ke kanan.
C. Braun
8

Cara paling elegan untuk menemukan persentase di seluruh kolom atau indeks adalah dengan menggunakan pd.crosstab.

Contoh data

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)]})

Dataframe keluarannya seperti ini

print(df)

        state   office_id   sales
    0   CA  1   764505
    1   WA  2   313980
    2   CO  3   558645
    3   AZ  4   883433
    4   CA  5   301244
    5   WA  6   752009
    6   CO  1   457208
    7   AZ  2   259657
    8   CA  3   584471
    9   WA  4   122358
    10  CO  5   721845
    11  AZ  6   136928

Cukup tentukan indeks, kolom, dan nilai yang akan digabungkan. Kata kunci normalisasi akan menghitung% di seluruh indeks atau kolom tergantung pada konteksnya.

result = pd.crosstab(index=df['state'], 
                     columns=df['office_id'], 
                     values=df['sales'], 
                     aggfunc='sum', 
                     normalize='index').applymap('{:.2f}%'.format)




print(result)
office_id   1   2   3   4   5   6
state                       
AZ  0.00%   0.20%   0.00%   0.69%   0.00%   0.11%
CA  0.46%   0.00%   0.35%   0.00%   0.18%   0.00%
CO  0.26%   0.00%   0.32%   0.00%   0.42%   0.00%
WA  0.00%   0.26%   0.00%   0.10%   0.00%   0.63%
ajknzhol.dll
sumber
3

Anda bisa sumkeseluruhan DataFramedan membaginya dengan statetotal:

# 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

Kembali

    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

Namun perhatikan bahwa ini hanya berfungsi karena semua kolom selain statenumerik, memungkinkan penjumlahan dari seluruh DataFrame. Misalnya, jika office_idadalah karakter, Anda mendapatkan error:

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

TypeError: jenis operan tidak didukung untuk /: 'str' dan 'str'

iggy
sumber
Saya mengedit untuk mencatat bahwa ini hanya berfungsi ketika semua kolom kecuali groupbykolom adalah numerik. Tapi sebaliknya cukup elegan. Apakah ada cara untuk membuatnya berfungsi dengan strkolom lain ?
Max Ghenis
Tidak sejauh yang saya tahu: stackoverflow.com/questions/34099684/…
iggy
2

Saya pikir ini akan melakukan trik dalam 1 baris:

df.groupby(['state', 'office_id']).sum().transform(lambda x: x/np.sum(x)*100)
louisD
sumber
Saya percaya itu mengambil semua kolom dari dataset. dalam hal ini, hanya ada satu. Jika Anda memiliki beberapa dan ingin melakukan operasi ini sekaligus, cukup tentukan setelah groupby ekspresi: df.groupby (['state', 'office_id']) [[NAMA KOLOM DI SINI]]. Etcetc jika Anda ingin agar kolom lain tidak tersentuh, cukup tetapkan kembali kolom spesifik
louisD
@louisD: Saya sangat menyukai pendekatan Anda yang mencoba membuatnya tetap pendek. Sayangnya, ketika saya mencoba untuk menetapkan kembali kolom seperti yang Anda sarankan, saya mendapatkan dua kesalahan: "ValueError: Buffer dtype mismatch, mengharapkan 'Python object' tetapi mendapat 'long long'", dan tambahan (selama penanganan pengecualian pertama): " TypeError: indeks kolom yang disisipkan tidak kompatibel dengan indeks bingkai "Kode yang saya gunakan adalah sebagai berikut: df ['persen'] = df.groupby (['state', 'office_id']). Sum (). Transform (lambda x: x / np.sum (x) * 100) Oleh karena itu, saya akan memposting jawaban terpisah untuk memperbaiki ini.
MightyCurious
1

Cara sederhana yang pernah saya gunakan adalah dengan menggabungkan 2 groupby kemudian melakukan pembagian sederhana.

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
Lemur terangkat
sumber
1
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)]})

grouped = df.groupby(['state', 'office_id'])
100*grouped.sum()/df[["state","sales"]].groupby('state').sum()

Pengembalian:

sales
state   office_id   
AZ  2   54.587910
    4   33.009225
    6   12.402865
CA  1   32.046582
    3   44.937684
    5   23.015735
CO  1   21.099989
    3   31.848658
    5   47.051353
WA  2   43.882790
    4   10.265275
    6   45.851935
Alessandro
sumber
0

Sebagai seseorang yang juga mempelajari panda, saya menemukan jawaban lain agak tersirat karena panda menyembunyikan sebagian besar pekerjaan di balik layar. Yakni bagaimana operasi bekerja dengan mencocokkan nama kolom dan indeks secara otomatis. Kode ini harus setara dengan versi langkah demi langkah dari jawaban yang diterima @ exp1orer

Dengan df, saya akan menyebutnya dengan alias state_office_sales:

                  sales
state office_id        
AZ    2          839507
      4          373917
      6          347225
CA    1          798585
      3          890850
      5          454423
CO    1          819975
      3          202969
      5          614011
WA    2          163942
      4          369858
      6          959285

state_total_salesadalah state_office_salesdikelompokkan berdasarkan jumlah nilai di index level 0(paling kiri).

In:   state_total_sales = df.groupby(level=0).sum()
      state_total_sales

Out: 
       sales
state   
AZ     2448009
CA     2832270
CO     1495486
WA     595859

Karena dua kerangka data berbagi nama indeks dan panda nama kolom akan menemukan lokasi yang sesuai melalui indeks bersama seperti:

In:   state_office_sales / state_total_sales

Out:  

                   sales
state   office_id   
AZ      2          0.448640
        4          0.125865
        6          0.425496
CA      1          0.288022
        3          0.322169
        5          0.389809
CO      1          0.206684
        3          0.357891
        5          0.435425
WA      2          0.321689
        4          0.346325
        6          0.331986

Untuk mengilustrasikan ini lebih baik lagi, berikut adalah jumlah parsial dengan a XXyang tidak memiliki padanan. Panda akan mencocokkan lokasi berdasarkan indeks dan nama kolom, di mana tidak ada panda yang tumpang tindih akan mengabaikannya:

In:   partial_total = pd.DataFrame(
                      data   =  {'sales' : [2448009, 595859, 99999]},
                      index  =             ['AZ',    'WA',   'XX' ]
                      )
      partial_total.index.name = 'state'


Out:  
         sales
state
AZ       2448009
WA       595859
XX       99999
In:   state_office_sales / partial_total

Out: 
                   sales
state   office_id   
AZ      2          0.448640
        4          0.125865
        6          0.425496
CA      1          NaN
        3          NaN
        5          NaN
CO      1          NaN
        3          NaN
        5          NaN
WA      2          0.321689
        4          0.346325
        6          0.331986

Ini menjadi sangat jelas ketika tidak ada indeks atau kolom bersama. Ini missing_index_totalssama dengan state_total_saleskecuali bahwa ia tidak memiliki nama-indeks.

In:   missing_index_totals = state_total_sales.rename_axis("")
      missing_index_totals

Out:  
       sales
AZ     2448009
CA     2832270
CO     1495486
WA     595859
In:   state_office_sales / missing_index_totals 

Out:  ValueError: cannot join with no overlapping index names
Anders Solberg
sumber
-1

Solusi satu baris:

df.join(
    df.groupby('state').agg(state_total=('sales', 'sum')),
    on='state'
).eval('sales / state_total')

Ini mengembalikan Seri rasio per kantor - dapat digunakan sendiri atau ditetapkan ke Dataframe asli.

ribitskiyb
sumber