Groupby

Pandas paketi veri analizi ve işlemesi için hazırlanmış bir Python paketidir. Bu paket çeşitli formatlardaki dosyaları (Excel, Csv, Txt gibi) okumayı, bu formatlarda dosyalar oluşturmayı kolaylaştırır. Aynı zamanda pandas paketi ile oluşturulan veri çerçeveleri (dataframe) sayesinde farklı formatlardaki (sayı, metin, tarih) veriyi birlikte tutmak, bu verileri işlemek ve basit analizler yapmak mümkündür.

Pandas paketinin altındaki bir yöntem olan groupby() ham haldeki veriden (örnek olarak alışveriş verisi) çeşitli istatistikler çıkarmak için oldukça yararlı bir araç. Bu Veri Defteri’nde UCI Machine Learning Repository’de bulunan online satış verisini kullanacağız. Veriye linke tıklayarak erişebilirsiniz. İlk olarak veriyi okutarak ve boyutlarına bakarak başlayalım.

In [1]:
# İlk adımda pandas ve numpy paketlerini yüklüyoruz
import pandas as pd
import numpy as np
from IPython.display import display, HTML
# Input dosyasının olduğu klasörün yolu
IDIR = 'D:/Data/Blog/Data/'

# Pandas'ın altındaki read_excel fonksiyonuyla Excel dosyasını okutabiliriz.
# Pandas aynı zamanda csv, table, sql ve kopyaladığınız tablo halindeki verileri de okuyabilir (clipboard).
# read_csv, read_table, read_json, read_sql, read_clipboard fonksiyonlarını inceleyebilirsiniz.
# Şimdi veriyi okutarak verinin boyutlarını inceleyelim.
df = pd.read_excel(IDIR + 'Online Retail.xlsx')

print("Veri çerçevesinin boyutu: " + str(np.shape(df)))
Veri çerçevesinin boyutu: (541909, 8)

Veride 8 kolon var. Kolonlar hakkındaki ilk bilgiyi head() fonksiyonuyla görebiliriz.

In [2]:
HTML(df.head().to_html())
Out[2]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom

Verideki kolonların ne anlama geldiğine bakalım.

Kolon Anlam
InvoiceNo Fatura numarası, her alışverişin bir numarası var
StockCode Ürünün numarası
Description Ürünün açıklaması
Quantity Üründen kaç adet alındığı
InvoiceDate Alışverişin tarihi ve saati
UnitPrice Ürünün birim fiyatı
CustomerID Müşteri numarası
Country Ülke

Öncelikle ülke bazında alışveriş istatistikleri çıkaralım. Bu amaçla ilk yapacağımız şey veri setini ülke bazında gruplamak.

In [3]:
# Veriyi ülke (Country) bazında grupluyoruz
df_country = df.groupby('Country')

Veride hangi ülkeler olduğunu görmek için group.keys(), kaç ülke olduğunu görmek içinse ngroups metotlarını kullanabiliriz.

In [4]:
print("Ülkeler")
print(df_country.groups.keys())
print("Ülke sayısı: " + str(df_country.ngroups))
Ülkeler
dict_keys(['Australia', 'Austria', 'Bahrain', 'Belgium', 'Brazil', 'Canada', 'Channel Islands', 'Cyprus', 'Czech Republic', 'Denmark', 'EIRE', 'European Community', 'Finland', 'France', 'Germany', 'Greece', 'Hong Kong', 'Iceland', 'Israel', 'Italy', 'Japan', 'Lebanon', 'Lithuania', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'RSA', 'Saudi Arabia', 'Singapore', 'Spain', 'Sweden', 'Switzerland', 'USA', 'United Arab Emirates', 'United Kingdom', 'Unspecified'])
Ülke sayısı: 38

Veri setinde 38 ülkeden yapılan alışverişler var. Her bir gruba (ülkede yapılan alışverişlere) erişmek için aşağıdaki gibi bir loop kullanabiliriz. df_country bir GroupBy objesi ve veriyi ülke ve satış verisini içeren bir dictionarye benzer bir şekilde tutuyor.

In [5]:
for country, sales in df_country:
    if country == 'Belgium':
        HTML(sales.head().to_html())

İstediğimiz gruba ulaşmanın bir diğer yolu da get_group() fonksiyonu. ‘Austria’ için olan satış verilerine ulaşmayı deneyelim.

In [6]:
HTML(df_country.get_group('Austria').head().to_html())
Out[6]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
31464 C538971 22153 ANGEL DECORATION STARS ON DRESS -48 2010-12-15 11:39:00 0.42 12865.0 Austria
34293 539330 37449 CERAMIC CAKE STAND + HANGING CAKES 8 2010-12-17 09:38:00 8.50 12370.0 Austria
34294 539330 37446 MINI CAKE STAND WITH HANGING CAKES 8 2010-12-17 09:38:00 1.45 12370.0 Austria
34295 539330 22962 JAM JAR WITH PINK LID 12 2010-12-17 09:38:00 0.85 12370.0 Austria
34296 539330 21428 SET3 BOOK BOX GREEN GINGHAM FLOWER 4 2010-12-17 09:38:00 4.25 12370.0 Austria

Şimdi her ülke için en çok satılan ürünü bulalım. Bunun için yapmamız gerekenler ülke (Country) ve ürün (StockCode) bazında satış miktarlarını (Quantity) toplamak olacak. Ondan sonra da her ülke için en çok satılan ürünü bulacağız. groupby fonksiyonuna vereceğimiz by argümanı hangi kolonlara göre (Country, StockCode) gruplayacağımız bilgisini veriyor. Daha sonra işlem yapacağımız kolonu (Quantity) ve yapacağımız işlemi (sum()) belirtiyoruz.

In [7]:
df_country_product = df.groupby(by = ['Country', 'StockCode'])['Quantity'].sum()
print(df_country_product.head())
Country    StockCode
Australia  15036        600
           20665          6
           20675        216
           20676        216
           20677        216
Name: Quantity, dtype: int64

Dataframe yapısını korumak için reset_index() fonksiyonunu kullanabiliriz.

In [8]:
df_country_product = df_country_product.reset_index()
HTML(df_country_product.head().to_html())
Out[8]:
Country StockCode Quantity
0 Australia 15036 600
1 Australia 20665 6
2 Australia 20675 216
3 Australia 20676 216
4 Australia 20677 216

Ülke bazında en yüksek miktara sahip ürünleri bulmak için groupbyı maksimum değeri bulacak şekilde kullanabiliriz. Ancak bu ürün bilgisini vermeyeceği için transform yöntemini kullanıyoruz. Bu sayede her ülke için maksimum satış değerine sahip ürünün satış adedine erişiyoruz. Aşağıdaki satırda değeri maksimum satışa sahip ürünlere sahip indexleri belirliyoruz. Bu, aynı satış miktarına sahip birden fazla ürün varsa onları da bulmamızı sağlar.

In [9]:
idx = df_country_product.groupby(['Country'])['Quantity'].transform(max) == df_country_product['Quantity']

HTML(df_country_product[idx].head(10).to_html())
Out[9]:
Country StockCode Quantity
221 Australia 22492 2916
671 Austria 21918 288
914 Bahrain 23076 96
1005 Belgium 21212 480
1705 Brazil 21430 24
1714 Brazil 22630 24
1716 Brazil 22697 24
1717 Brazil 22698 24
1718 Brazil 22699 24
1719 Brazil 22722 24

Şimdi de müşteriler için çeşitli istatistikler çıkaralım. Müşterilerle ilgili pazarlama, segmentasyon, müşteri terki tahmini gib çalışmalarda sıklıkla kullanılan RFM (Recency, frequency, monetary / yakınlık, sıklık, mali) değişkenlerini groupby fonksiyonunu kullanarak oluşturacağız. Bu tip istatistikler aynı zamanda gerçekleştireceğiniz çalışmalarda öznitelik türetmek için de kullanılabilir.

Her müşteri için aşağıdaki değişkenlerin değerlerini elde etmek istiyoruz:

  • Alışveriş sayısı
  • Toplam ve ortalama alışveriş miktarı
  • Satın alınan toplam ve ortalama (farklı) ürün sayısı
  • En son alışveriş tarihi

Bunun için öncelike toplam alışveriş miktarını ‘Amount’ kolonuna yazdıracağız. Yapmamız gereken miktar (Quantity) ile birim fiyat (UnitPrice) kolonlarını çarpmak.

Bunları yapmadan önce müşteri (CustomerID) kolonunda boş değerler olduğu için öncelikle bu alışverişleri veri setinden çıkaralım.

In [10]:
# CustomerID değeri olmayan müşterileri veri setinden çıkaralım.
print("Temizlik öncesi alışveriş sayısı: " + str(len(df)))
df = df[df['CustomerID'] > 0]
print("Temizlik sonrası alışveriş sayısı: " + str(len(df)))

# Birim fiyat ve miktar değerlerini çarparak toplam tutarı bulalım.
df['Amount'] = df['Quantity'] * df['UnitPrice']
HTML(df.head().to_html())
Temizlik öncesi alışveriş sayısı: 541909
Temizlik sonrası alışveriş sayısı: 406829
C:\Users\byuceoglu\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:7: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  import sys
Out[10]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country Amount
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom 15.30
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom 22.00
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34

Alışverişleri önce müşteri (CustomerID) ve alışveriş (InvoiceNo) değerleri için gruplayalım. Bu sayede her alışveriş için sepetteki ürün sayısı ve sepetteki ürünleri toplam tutarını öğrenebiliriz. Her alışveriş için yapacağımız işlemler aşağıdakilerdir:

  • Farklı ürün sayısını bulmak (StockCode): Kullanacağımız yöntem nunique kaç tane tekil (unique) değer olduğuna bakar.
  • Sepetteki ürünlerin değerini toplamak (Amount): Kullanacağımız yöntem sum verilen kolonun değerlerini toplar.
  • Alışveriş tarihini belirlemek (InvoiceDate): Kullanacağımız değer max maksimum değeri verir. Burada max değerini kullanmamız şart değil. Sadece bu değeri bir sonraki tabloya da aktarmak istiyoruz.

agg fonksiyonu dictionary yapısıyla bu farklı işlemleri tek satırda yapmamıza imkan tanıyor.

In [11]:
df_customer = df.groupby(['CustomerID', 'InvoiceNo']).agg({'StockCode':'nunique', 
                                                           'Amount':'sum', 
                                                           'InvoiceDate': 'max'}).reset_index()

HTML(df_customer.head().to_html())
Out[11]:
CustomerID InvoiceNo StockCode Amount InvoiceDate
0 12346.0 541431 1 77183.60 2011-01-18 10:01:00
1 12346.0 C541433 1 -77183.60 2011-01-18 10:17:00
2 12347.0 537626 31 711.79 2010-12-07 14:57:00
3 12347.0 542237 29 475.39 2011-01-26 14:30:00
4 12347.0 549222 24 636.25 2011-04-07 10:43:00

Artık istediğimiz değerleri hesaplayabiliriz. Her müşteri ve alışveriş için oluşturduğumuz tabloyu müşteri bazında tekrar gruplayacağız. Yapmak istediğimiz işlemler aşağıdakilerdir:

  • İşlem sayısını bulmak: ‘InvoiceNo’ kolonundaki değerleri sayacağız. Değerler tekil olduğu için count fonksiyonunu kullanabiliriz.
  • Toplam ve ortalam işlem miktarını bulmak: ‘Amount’ kolonu için toplam ve ortalama (mean) değerleri bulacağız.
  • En son alışveriş tarihini bulmak: Her müşteri için ‘InvoiceDate’ kolonundaki maksimum değeri bulacağız.

Miktar kolonunda yapacağımız iki işlem (toplam ve ortalama) için de dictionary yapısını ya da bir liste kullanabiliriz.

In [12]:
df_customer = df_customer.groupby('CustomerID').agg({'InvoiceNo':'count', 
                                                     'Amount': ['sum','mean'],
                                                    'InvoiceDate': 'max'}).reset_index()

df_customer.columns = ['CustomerID', 'Number_of_Transactions', 'Total_Amount', 'Average_Amount', 'Last_Transaction_Date']
HTML(df_customer.head().to_html())
Out[12]:
CustomerID Number_of_Transactions Total_Amount Average_Amount Last_Transaction_Date
0 12346.0 2 0.00 0.000000 2011-01-18 10:17:00
1 12347.0 7 4310.00 615.714286 2011-12-07 15:52:00
2 12348.0 4 1797.24 449.310000 2011-09-25 13:13:00
3 12349.0 1 1757.55 1757.550000 2011-11-21 09:51:00
4 12350.0 1 334.40 334.400000 2011-02-02 16:01:00

groupby()fonksiyonunu kullanarak veriyi özetledik. Bu noktadan sonra uygulamanıza göre veriden özetlediğiniz bilgileri kullanabilirsiniz.

Jupyter Notebook dosyalarına ulaşmak için Github dizinine bakabilirsiniz.

4 comments

  1. Merhaba,

    “df_country bir GroupBy objesi ve veriyi ülke ve satış verisini içeren bir dictionarye benzer bir şekilde tutuyor.” dedikten sonraki kodda, “for country, sales in df_country:” loop’undaki “sales” nereden geliyor? 8 kolondan hiç birinde “sales” ismi göremedim, yani df_country’nin, veriyi “country, sales” şeklinde tuttuğunu nereden anladık?

    Teşekkürler.

    1. Merhaba,

      “df_country” sözlük yapısına sahip bir değişken. Burada “sales” de “country” de bizim o döngüde oluşturduğumuz değişkenler. İsmini istediğiniz gibi oluşturabilirsiniz. Burada önemli olan “country” değişkeninin anahtara (key), “sales” değişkeninin ise değere (value) denk gelmesi. Kodun son satırını print(sales.head()) şeklinde değiştirirseniz “sales” değişkeninin ülkesi “Belgium” olan satırlara denk geldiğini görebilirsiniz. Daha fazla detay için Kaan’ın sözlük metodları yazısını öneririm.

      İyi günler

      1. Anladım, ben yeni oluşturduğumuzu anlamamışım, kolon isimlerinde “country” olunca sanki bir de “sales” var da, zaten varolan bir veriyi çağırıyomuşuz gibi algılamışım , Python’a da yeni başladım sayılır, “for” kullanımını anlamamışım sanırım, tekrar bakayım, önerdiğiniz yazıya da bakıyorum, teşekkür ederim cevabınız için, iyi çalışmalar.

Leave a Reply

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

This site uses Akismet to reduce spam. Learn how your comment data is processed.