BigQuery DataFrames paketi kullanılarak Iowa'daki içki satışlarının keşifsel veri analizi

BigQuery DataFrames paketi kullanılarak Iowa'daki içki satışlarının keşifsel veri analizi

Bu codelab hakkında

subjectSon güncelleme Ara 4, 2024
account_circleYazan: Tim Swena

1. Genel Bakış

Bu laboratuvarda, Iowa'daki içki satışları herkese açık veri kümesini temizlemek ve analiz etmek için BigQuery Studio'daki bir Python not defterinden BigQuery veri çerçevelerini kullanacaksınız. Analizler keşfetmek için BigQuery ML ve uzak işlev özelliklerinden yararlanın.

Coğrafi bölgelerdeki satışları karşılaştırmak için bir Python not defteri oluşturacaksınız. Bu, herhangi bir yapılandırılmış veri üzerinde çalışacak şekilde uyarlanabilir.

Bu laboratuvarda, aşağıdaki görevleri nasıl gerçekleştireceğinizi öğreneceksiniz:

  • BigQuery Studio'da Python not defterlerini etkinleştirme ve kullanma
  • BigQuery DataFrames paketini kullanarak BigQuery'ye bağlanma
  • BigQuery ML'yi kullanarak doğrusal regresyon oluşturma
  • Pandas'a benzer tanıdık bir söz dizimi kullanarak karmaşık toplama ve birleştirme işlemleri gerçekleştirebilirsiniz.

2. Şartlar

  • Chrome veya Firefox gibi bir tarayıcı
  • Faturalandırmanın etkin olduğu bir Google Cloud projesi

Başlamadan önce

Bu kod laboratuvarındaki talimatları uygulamak için BigQuery Studio'nun etkinleştirildiği ve bağlı bir faturalandırma hesabının bulunduğu bir Google Cloud projesine ihtiyacınız vardır.

  1. Google Cloud Console'daki proje seçici sayfasında bir Google Cloud projesi seçin veya oluşturun.
  2. Google Cloud projeniz için faturalandırmanın etkinleştirildiğinden emin olun. Projede faturalandırmanın etkin olup olmadığını nasıl kontrol edeceğinizi öğrenin.
  3. Öğe yönetimi için BigQuery Studio'yu etkinleştirme talimatlarını uygulayın.

BigQuery Studio'yu hazırlama

Boş bir not defteri oluşturup bir çalışma zamanına bağlayın.

  1. Google Cloud Console'da BigQuery Studio'ya gidin.
  2. + düğmesinin yanındaki simgesini tıklayın.
  3. Python not defteri'ni seçin.
  4. Şablon seçiciyi kapatın.
  5. Yeni bir kod hücresi oluşturmak için + Kod'u seçin.
  6. BigQuery DataFrames paketinin en son sürümünü kod hücresinden yükleyin.Aşağıdaki komutu yazın.
    %pip install --upgrade bigframes --quiet
    Kod hücresini çalıştırmak için Hücreyi çalıştır düğmesini tıklayın veya Üst Karakter + Enter tuşlarına basın.

3. Herkese açık veri kümesini okuma

Yeni bir kod hücresinde aşağıdakileri çalıştırarak BigQuery DataFrames paketini başlatın:

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"
bpd.options.display.repr_mode = "deferred"

Not: Bu eğitimde, deneysel "kısmi sıralama modunu" kullanıyoruz. Bu mod, pandas benzeri filtrelemeyle birlikte kullanıldığında daha verimli sorgulara olanak tanır. Sıkı bir sıralama veya dizin gerektiren bazı pandas özellikleri çalışmayabilir.

bigframes paket sürümünüzü şu şekilde kontrol edebilirsiniz:

bpd.__version__

Bu eğitim için 1.27.0 veya daha yeni bir sürüm gereklidir.

Iowa'da alkollü içecek perakende satışları

Iowa'daki alkollü içecek perakende satışları veri kümesi, Google Cloud'un herkese açık veri kümesi programı aracılığıyla BigQuery'de sağlanır. Bu veri kümesi, 1 Ocak 2012'den bu yana Iowa Eyaleti'nde perakendeciler tarafından bireylere satmak üzere yapılan tüm toptan alkol satın alma işlemlerini içerir. Veriler, Iowa Ticaret Bakanlığı'ndaki Alkollü İçecekler Bölümü tarafından toplanır.

Iowa'daki alkol perakende satışlarını analiz etmek için BigQuery'de bigquery-public-data.iowa_liquor_sales.sales tablosunu sorgulayın. Sorgu dizesinden veya tablo kimliğinden DataFrame oluşturmak için bigframes.pandas.read_gbq() yöntemini kullanın.

"df" adlı bir DataFrame oluşturmak için yeni bir kod hücresinde aşağıdakileri çalıştırın:

df = bpd.read_gbq_table("bigquery-public-data.iowa_liquor_sales.sales")

Veri çerçevesi hakkında temel bilgileri keşfetme

Verilerin küçük bir örneğini indirmek için DataFrame.peek() yöntemini kullanın.

Bu hücreyi çalıştırın:

df.peek()

Beklenen çıkış:

index   invoice_and_item_number date    store_number    store_name      ...
0       RINV-04620300080        2023-04-28      10197   SUNSHINE FOODS / HAWARDEN      
1       RINV-04864800097        2023-09-25      2621    HY-VEE FOOD STORE #3 / SIOUX CITY      
2       RINV-05057200028        2023-12-28      4255    FAREWAY STORES #058 / ORANGE CITY      
3       ...                            

Not: head(), sıralama gerektirir ve bir veri örneğini görselleştirmek istiyorsanız genellikle peek()'ten daha az etkilidir.

Pandas'da olduğu gibi, mevcut tüm sütunları ve bunlara karşılık gelen veri türlerini görmek için DataFrame.dtypes mülkünü kullanın. Bunlar pandas ile uyumlu bir şekilde gösterilir.

Bu hücreyi çalıştırın:

df.dtypes

Beklenen çıkış:

invoice_and_item_number string[pyarrow]
date    date32[day][pyarrow]
store_number    string[pyarrow]
store_name      string[pyarrow]
address string[pyarrow]
city    string[pyarrow]
zip_code        string[pyarrow]
store_location  geometry
county_number   string[pyarrow]
county  string[pyarrow]
category        string[pyarrow]
category_name   string[pyarrow]
vendor_number   string[pyarrow]
vendor_name     string[pyarrow]
item_number     string[pyarrow]
item_description        string[pyarrow]
pack    Int64
bottle_volume_ml        Int64
state_bottle_cost       Float64
state_bottle_retail     Float64
bottles_sold    Int64
sale_dollars    Float64
volume_sold_liters      Float64
volume_sold_gallons     Float64

dtype: object

DataFrame.describe() yöntemi, DataFrame'den bazı temel istatistikleri sorgulayarak döndürür. Bu özet istatistikleri pandas DataFrame olarak indirmek için DataFrame.to_pandas() komutunu çalıştırın.

Bu hücreyi çalıştırın:

df.describe("all").to_pandas()

Beklenen çıkış:

        invoice_and_item_number date    store_number    store_name      ...
nunique
30305765        <NA>    3158    3353    ...
std    
<NA>    <NA>    <NA>    <NA>    ...
mean    
<NA>    <NA>    <NA>    <NA>    ...
75%     <NA>    <NA>    <NA>    <NA>    ...
25%     <NA>    <NA>    <NA>    <NA>    ...
count  
30305765        <NA>    30305765        30305765        ...
min    
<NA>    <NA>    <NA>    <NA>    ...
50%     <NA>    <NA>    <NA>    <NA>    ...
max    
<NA>    <NA>    <NA>    <NA>    ...
9 rows × 24 columns

4. Verileri görselleştirme ve temizleme

Iowa'daki perakende satışlarda alkollü içecek verilerinin yer aldığı veri kümesi, perakende mağazaların bulunduğu yerler de dahil olmak üzere ayrıntılı coğrafi bilgiler sağlar. Coğrafi bölgelerdeki trendleri ve farklılıkları belirlemek için bu verilerden yararlanın.

Posta kodu başına satışları görselleştirme

DataFrame.plot.hist() gibi çeşitli yerleşik görselleştirme yöntemleri vardır. Alkol satışlarını posta koduna göre karşılaştırmak için bu yöntemi kullanın.

volume_by_zip = df.groupby("zip_code").agg({"volume_sold_liters": "sum"})
volume_by_zip
.plot.hist(bins=20)

Beklenen çıkış:

Hacim histogramı

En çok alkol satılan posta kodlarını görmek için çubuk grafik kullanın.

(
  volume_by_zip
 
.sort_values("volume_sold_liters", ascending=False)
 
.head(25)
 
.to_pandas()
 
.plot.bar(rot=80)
)

Beklenen çıkış:

En çok satış yapılan posta kodlarındaki alkol hacminin çubuk grafiği

Verileri temizleme

Bazı posta kodlarının sonuna .0 eklenir. Posta kodları, veri toplama sürecinde bir noktada yanlışlıkla kayan nokta değerlerine dönüştürülmüş olabilir. Posta kodlarını temizlemek ve analizi tekrarlamak için normal ifadeleri kullanın.

df = (
    bpd.read_gbq_table("bigquery-public-data.iowa_liquor_sales.sales")
    .assign(
        zip_code=lambda _: _["zip_code"].str.replace(".0", "")
    )
)
volume_by_zip = df.groupby("zip_code").agg({"volume_sold_liters": "sum"})
(
  volume_by_zip
  .sort_values("volume_sold_liters", ascending=False)
  .head(25)
  .to_pandas()
  .plot.bar(rot=80)
)

Beklenen çıkış:

En çok satış yapılan posta kodlarındaki alkol hacminin çubuk grafiği

5. Satışlardaki korelasyonları keşfetme

Bazı posta kodlarında satışlar neden diğerlerinden daha fazla? Bunun nüfus büyüklüğündeki farklılıklardan kaynaklandığına dair bir hipotez var. Nüfusu daha fazla olan bir posta kodunda daha fazla alkol satılır.

Nüfus ile içki satış hacmi arasındaki korelasyon katsayısını hesaplayarak bu hipotezi test edin.

Diğer veri kümeleriyle birleştirme

ABD Nüfus Sayımı Bürosu'nun Amerikan Topluluk Anketi posta kodu tabulation alanı anketi gibi bir nüfus veri kümesiyle birleştirin.

census_acs = bpd.read_gbq_table("bigquery-public-data.census_bureau_acs.zcta_2020_5yr")

American Community Survey, eyaletleri GEOID'ye göre tanımlar. Posta kodu tabulation alanları söz konusu olduğunda GEOID, posta koduna eşittir.

volume_by_pop = volume_by_zip.join(
    census_acs
.set_index("geo_id")
)

Posta kodu tabulation alanı nüfuslarını satılan alkol litreleriyle karşılaştırmak için bir dağılım grafiği oluşturun.

(
    volume_by_pop
[["volume_sold_liters", "total_pop"]]
   
.to_pandas()
   
.plot.scatter(x="total_pop", y="volume_sold_liters")
)

Beklenen çıkış:

Nüfus ve satılan litrelik alkole göre posta kodu tabülasyon alanlarının dağılım grafiği

Korelasyonları hesaplama

Trend yaklaşık olarak doğrusal görünüyor. Nüfus verilerinin, içki satışlarını ne kadar iyi tahmin edebildiğini kontrol etmek için bu verilere doğrusal regresyon modeli uygulayın.

from bigframes.ml.linear_model import LinearRegression

feature_columns = volume_by_pop[["total_pop"]]
label_columns = volume_by_pop[["volume_sold_liters"]]

# Create the linear model
model = LinearRegression()
model.fit(feature_columns, label_columns)

score yöntemini kullanarak eşleşmenin ne kadar iyi olduğunu kontrol edin.

model.score(feature_columns, label_columns).to_pandas()

Örnek çıkış:

        mean_absolute_error     mean_squared_error      mean_squared_log_error  median_absolute_error   r2_score        explained_variance
0       245065.664095   224398167097.364288     5.595021        178196.31289    0.380096        0.380096

En iyi uyum çizgisini çizin ancak predict işlevini bir popülasyon değeri aralığında çağırın.

import matplotlib.pyplot as pyplot
import numpy as np
import pandas as pd

line = pd.Series(np.arange(0, 50_000), name="total_pop")
predictions = model.predict(line).to_pandas()

zips = volume_by_pop[["volume_sold_liters", "total_pop"]].to_pandas()
pyplot.scatter(zips["total_pop"], zips["volume_sold_liters"])
pyplot.plot(
 
line,
 
predictions.sort_values("total_pop")["predicted_volume_sold_liters"],
 
marker=None,
 
color="red",
)

Beklenen çıkış:

En iyi uyum çizgisi içeren dağılım grafiği

Heteroskedastisiteyi ele alma

Önceki grafikteki veriler heteroskedastik görünüyor. En iyi uyum doğrusu etrafındaki varyans, popülasyonla birlikte büyür.

Kişi başına satın alınan alkol miktarı nispeten sabit olabilir.

volume_per_pop = (
    volume_by_pop[volume_by_pop['total_pop'] > 0]
    .assign(liters_per_pop=lambda df: df["volume_sold_liters"] / df["total_pop"])
)

(
    volume_per_pop[["liters_per_pop", "total_pop"]]
    .to_pandas()
    .plot.scatter(x="total_pop", y="liters_per_pop")
)

Beklenen çıkış:

Nüfus başına litrelik dağılım grafiği

Satın alınan ortalama alkol litre sayısını iki farklı şekilde hesaplayabilirsiniz:

  1. Iowa'da kişi başına ortalama ne kadar alkol satın alınıyor?
  2. Tüm posta kodları için kişi başına satın alınan alkol miktarının ortalaması.

(1)'de, eyalet genelinde ne kadar alkol satın alındığını gösterir. (2)'de ise ortalama posta kodu gösterilir. Farklı posta kodlarının farklı nüfusları olduğundan bu kod (1) ile aynı olmayabilir.

df = (
    bpd.read_gbq_table("bigquery-public-data.iowa_liquor_sales.sales")
    .assign(
        zip_code=lambda _: _["zip_code"].str.replace(".0", "")
    )
)
census_state = bpd.read_gbq(
    "bigquery-public-data.census_bureau_acs.state_2020_5yr",
    index_col="geo_id",
)

volume_per_pop_statewide = (
    df['volume_sold_liters'].sum()
    / census_state["total_pop"].loc['19']
)
volume_per_pop_statewide

Beklenen çıkış: 87.997

average_per_zip = volume_per_pop["liters_per_pop"].mean()
average_per_zip

Beklenen çıkış: 67.139

Yukarıdakine benzer şekilde bu ortalamaları çizin.

import numpy as np
import pandas as pd
from matplotlib import pyplot

line = pd.Series(np.arange(0, 50_000), name="total_pop")

zips = volume_per_pop[["liters_per_pop", "total_pop"]].to_pandas()
pyplot.scatter(zips["total_pop"], zips["liters_per_pop"])
pyplot.plot(line, np.full(line.shape, volume_per_pop_statewide), marker=None, color="magenta")
pyplot.plot(line, np.full(line.shape, average_per_zip), marker=None, color="red")

Beklenen çıkış:

Nüfus başına litrelik dağılım grafiği

Özellikle nüfusu daha az olan bölgelerde, oldukça büyük aykırı değerlere sahip bazı posta kodları hâlâ mevcuttur. Bunun nedenini tahmin etmek size bırakılmıştır. Örneğin, bazı posta kodlarında nüfus düşük olabilir ancak bölgedeki tek içki mağazası bu posta kodunda olduğu için tüketim yüksek olabilir. Bu durumda, çevredeki posta kodlarının nüfusuna göre hesaplama yapmak bu aykırı değerleri bile ortadan kaldırabilir.

6. Satılan içki türlerini karşılaştırma

Iowa'daki alkollü içecek perakende satışları veritabanı, coğrafi verilere ek olarak satılan ürünle ilgili ayrıntılı bilgiler de içerir. Bu verileri analiz ederek coğrafi bölgelerdeki beğeni farklılıklarını ortaya çıkarabiliriz.

Kategorileri keşfedin

Öğeler veritabanında kategorilere ayrılır. Kaç kategori var?

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"
bpd.options.display.repr_mode = "deferred"

df = bpd.read_gbq_table("bigquery-public-data.iowa_liquor_sales.sales")
df.category_name.nunique()

Beklenen çıkış: 103

Hacime göre en popüler kategoriler hangileri?

counts = (
    df
.groupby("category_name")
   
.agg({"volume_sold_liters": "sum"})
   
.sort_values(["volume_sold_liters"], ascending=False)
   
.to_pandas()
)
counts
.head(25).plot.bar(rot=80)

Satılan en popüler içki kategorilerinin çubuk grafiği

ARRAY veri türüyle çalışma

Viski, rom, votka ve daha birçok kategori vardır. Bunları bir şekilde gruplandırmak istiyorum.

Series.str.split() yöntemini kullanarak kategori adlarını ayrı kelimelere ayırarak başlayın. explode() yöntemini kullanarak bu yöntemin oluşturduğu diziyi iç içe yerleştirmeyi kaldırın.

category_parts = df.category_name.str.split(" ").explode()
counts
= (
    category_parts
   
.groupby(category_parts)
   
.size()
   
.sort_values(ascending=False)
   
.to_pandas()
)
counts
.head(25).plot.bar(rot=80)

Kategorilerdeki kelime sayısına göre kelimeler

category_parts.nunique()

Beklenen çıkış: 113

Yukarıdaki grafiğe bakıldığında, verilerde VODKA'nın VODKAS'tan ayrı olduğu görülüyor. Kategorileri daha küçük bir kümeye daraltmak için daha fazla gruplandırma gerekir.

7. NLTK&#39;yi BigQuery veri çerçeveleriyle kullanma

Yalnızca yaklaşık 100 kategori olduğunda, bazı sezgisel kurallar yazmak veya hatta kategoriden daha geniş bir içki türüne manuel olarak eşleme oluşturmak mümkün olabilir. Alternatif olarak, bu tür bir eşleme oluşturmak için Gemini gibi büyük bir dil modeli de kullanılabilir. BigQuery DataFrames'i Gemini ile kullanmak için BigQuery DataFrames'i kullanarak yapılandırılmamış verilerden analiz elde etme kod laboratuvarını deneyin.

Bunun yerine, bu verileri işlemek için daha geleneksel bir doğal dil işleme paketi olan NLTK'yi kullanın. "Kelime kökü çıkarıcı" adı verilen teknoloji, çoğul ve tekil adları aynı değerde birleştirebilir.

Kelimeleri köklerine ayırmak için NLTK'yi kullanma

NLTK paketi, Python'dan erişilebilen doğal dil işleme yöntemleri sağlar. Denemek için paketi yükleyin.

%pip install nltk

Ardından paketi içe aktarın. Sürümü inceleyin. Bu, eğitimde daha sonra kullanılacaktır.

import nltk

nltk.__version__

Kelimelerin kökünü alarak standartlaştırmanın bir yolu. Bu işlem, çoğul ifadeler için sona eklenen "s" gibi tüm son ekleri kaldırır.

def stem(word: str) -> str:
   
# https://www.nltk.org/howto/stem.html
   
import nltk.stem.snowball

   
# Avoid failure if a NULL is passed in.
   
if not word:
       
return word

   
stemmer = nltk.stem.snowball.SnowballStemmer("english")
   
return stemmer.stem(word)

Bunu birkaç kelimede deneyin.

stem("WHISKEY")

Beklenen çıkış: whiskey

stem("WHISKIES")

Beklenen çıkış: whiski

Maalesef bu işlem, viskileri aynı şekilde eşleştirmedi. Kök ayırıcılar, düzensiz çoğullar için iyi çalışmaz. "Lemma" adı verilen temel kelimeyi tanımlamak için daha gelişmiş teknikler kullanan bir lemmatizer kullanmayı deneyin.

def lemmatize(word: str) -> str:
   
# https://stackoverflow.com/a/18400977/101923
   
# https://www.nltk.org/api/nltk.stem.wordnet.html#module-nltk.stem.wordnet
   
import nltk
   
import nltk.stem.wordnet


   
# Avoid failure if a NULL is passed in.
   
if not word:
       
return word

   
nltk.download('wordnet')
   
wnl = nltk.stem.wordnet.WordNetLemmatizer()
   
return wnl.lemmatize(word.lower())

Bunu birkaç kelimede deneyin.

lemmatize("WHISKIES")

Beklenen çıkış: whisky

lemmatize("WHISKY")

Beklenen çıkış: whisky

lemmatize("WHISKEY")

Beklenen çıkış: whiskey

Maalesef bu lemmatizer, "whiskey"yi "whiskies" ile aynı lemma ile eşlemiyor. Bu kelime özellikle Iowa perakende alkol satışları veritabanı için önemli olduğundan, sözlük kullanarak kelimeyi Amerikan yazımıyla eşleyin.

def lemmatize(word: str) -> str:
   
# https://stackoverflow.com/a/18400977/101923
   
# https://www.nltk.org/api/nltk.stem.wordnet.html#module-nltk.stem.wordnet
   
import nltk
   
import nltk.stem.wordnet


   
# Avoid failure if a NULL is passed in.
   
if not word:
       
return word

   
nltk.download('wordnet')
   
wnl = nltk.stem.wordnet.WordNetLemmatizer()
   
lemma = wnl.lemmatize(word.lower())

   
table = {
       
"whisky": "whiskey",  # Use the American spelling.
   
}
   
return table.get(lemma, lemma)

Bunu birkaç kelimede deneyin.

lemmatize("WHISKIES")

Beklenen çıkış: whiskey

lemmatize("WHISKEY")

Beklenen çıkış: whiskey

Tebrikler! Bu lemmatizer, kategorileri daraltmak için iyi sonuç verecektir. BigQuery ile kullanmak için modeli buluta dağıtmanız gerekir.

Projenizi işlev dağıtımı için ayarlama

BigQuery'nin bu işleve erişebilmesi için bunu buluta dağıtmadan önce bazı tek seferlik ayarlar yapmanız gerekir.

Yeni bir kod hücresi oluşturun ve your-project-id yerine bu eğitim için kullandığınız Google Cloud proje kimliğini yazın.

project_id = "your-project-id"

Bu işlevin herhangi bir bulut kaynağına erişmesi gerekmediğinden, izinsiz bir hizmet hesabı oluşturun.

from google.cloud import iam_admin_v1
from google.cloud.iam_admin_v1 import types

iam_admin_client = iam_admin_v1.IAMClient()
request = types.CreateServiceAccountRequest()

account_id = "bigframes-no-permissions"
request.account_id = account_id
request.name = f"projects/{project_id}"

display_name = "bigframes remote function (no permissions)"
service_account = types.ServiceAccount()
service_account.display_name = display_name
request.service_account = service_account

account = iam_admin_client.create_service_account(request=request)
print(account.email)

Beklenen çıkış: bigframes-no-permissions@your-project-id.iam.gserviceaccount.com

İşlevi barındıracak bir BigQuery veri kümesi oluşturun.

from google.cloud import bigquery

bqclient = bigquery.Client(project=project_id)
dataset = bigquery.Dataset(f"{project_id}.functions")
bqclient.create_dataset(dataset, exists_ok=True)

Uzaktan işlev dağıtma

Henüz etkinleştirilmediyse Cloud Functions API'yi etkinleştirin.

!gcloud services enable cloudfunctions.googleapis.com

Ardından, işlevinizi yeni oluşturduğunuz veri kümesine dağıtın. Önceki adımlarda oluşturduğunuz işleve bir @bpd.remote_function süsleyici ekleyin.

@bpd.remote_function(
   
dataset=f"{project_id}.functions",
   
name="lemmatize",
   
# TODO: Replace this with your version of nltk.
   
packages=["nltk==3.9.1"],
   
cloud_function_service_account=f"bigframes-no-permissions@{project_id}.iam.gserviceaccount.com",
   
cloud_function_ingress_settings="internal-only",
)
def lemmatize(word: str) -> str:
   
# https://stackoverflow.com/a/18400977/101923
   
# https://www.nltk.org/api/nltk.stem.wordnet.html#module-nltk.stem.wordnet
   
import nltk
   
import nltk.stem.wordnet


   
# Avoid failure if a NULL is passed in.
   
if not word:
       
return word

   
nltk.download('wordnet')
   
wnl = nltk.stem.wordnet.WordNetLemmatizer()
   
lemma = wnl.lemmatize(word.lower())

   
table = {
       
"whisky": "whiskey",  # Use the American spelling.
   
}
   
return table.get(lemma, lemma)

Dağıtım işlemi yaklaşık iki dakika sürer.

Uzaktan kumanda işlevlerini kullanma

Dağıtım tamamlandıktan sonra bu işlevi test edebilirsiniz.

lemmatize = bpd.read_gbq_function(f"{project_id}.functions.lemmatize")

words
= bpd.Series(["whiskies", "whisky", "whiskey", "vodkas", "vodka"])
words
.apply(lemmatize).to_pandas()

Beklenen çıkış:

0       whiskey
1       whiskey
2       whiskey
3       vodka
4       vodka

dtype: string

8. Alkol tüketimini ilçeye göre karşılaştırma

lemmatize işlevi kullanıma sunulduğundan kategorileri birleştirmek için bu işlevi kullanabilirsiniz.

Kategoriyi en iyi şekilde özetleyecek kelimeyi bulma

Öncelikle, veritabanındaki tüm kategorilerin bir DataFrame'ini oluşturun.

df = bpd.read_gbq_table("bigquery-public-data.iowa_liquor_sales.sales")

categories
= (
    df
['category_name']
   
.groupby(df['category_name'])
   
.size()
   
.to_frame()
   
.rename(columns={"category_name": "total_orders"})
   
.reset_index(drop=False)
)
categories
.to_pandas()

Beklenen çıkış:

category_name   total_orders
0       100 PROOF VODKA 99124
1       100% AGAVE TEQUILA      724374
2       AGED DARK RUM   59433
3       AMARETTO - IMPORTED     102
4       AMERICAN ALCOHOL        24351
...     ...     ...
98      WATERMELON SCHNAPPS     17844
99      WHISKEY LIQUEUR 1442732
100     WHITE CREME DE CACAO    7213
101     WHITE CREME DE MENTHE   2459
102     WHITE RUM       436553
103 rows × 2 columns

Ardından, noktalama işaretleri ve "öğe" gibi birkaç doldurma kelimesi hariç kategorilerdeki tüm kelimelerin bir DataFrame'ini oluşturun.

words = (
    categories.assign(
        words=categories['category_name']
        .str.lower()
        .str.split(" ")
    )
    .assign(num_words=lambda _: _['words'].str.len())
    .explode("words")
    .rename(columns={"words": "word"})
)
words = words[
    # Remove punctuation and "item", unless it's the only word
    (words['word'].str.isalnum() & ~(words['word'].str.startswith('item')))
    | (words['num_words'] == 1)
]
words.to_pandas()

Beklenen çıkış:

category_name   total_orders    word    num_words
0       100 PROOF VODKA 99124   100     3
1       100 PROOF VODKA 99124   proof   3
2       100 PROOF VODKA 99124   vodka   3
...     ...     ...     ...     ...
252     WHITE RUM       436553  white   2
253     WHITE RUM       436553  rum     2
254 rows × 4 columns

Gruplandırmadan sonra lemma oluşturarak Cloud işlevinizdeki yükü azalttığınızı unutmayın. Lemmatize işlevini veritabanındaki birkaç milyon satırın her birine uygulamak mümkündür ancak bu, gruplandırmadan sonra uygulamaktan daha maliyetli olur ve kota artışları gerektirebilir.

lemmas = words.assign(lemma=lambda _: _["word"].apply(lemmatize))
lemmas.to_pandas()

Beklenen çıkış:

category_name   total_orders    word    num_words       lemma
0       100 PROOF VODKA 99124   100     3       100
1       100 PROOF VODKA 99124   proof   3       proof
2       100 PROOF VODKA 99124   vodka   3       vodka
...     ...     ...     ...     ...     ...
252     WHITE RUM       436553  white   2       white
253     WHITE RUM       436553  rum     2       rum
254 rows × 5 columns

Kelimeler lemmalaştırıldı. Artık kategoriyi en iyi özetleyen lemmayı seçmeniz gerekiyor. Kategorilerde çok fazla işlev kelimesi olmadığından, bir kelimenin birden fazla kategoride görünmesi, muhtemelen özetleyici bir kelime olarak kullanılması gerektiğine dair sezgisel kuraldan yararlanın (ör. viski).

lemma_counts = (
    lemmas
   
.groupby("lemma", as_index=False)
   
.agg({"total_orders": "sum"})
   
.rename(columns={"total_orders": "total_orders_with_lemma"})
)

categories_with_lemma_counts
= lemmas.merge(lemma_counts, on="lemma")

max_lemma_count
= (
    categories_with_lemma_counts
   
.groupby("category_name", as_index=False)
   
.agg({"total_orders_with_lemma": "max"})
   
.rename(columns={"total_orders_with_lemma": "max_lemma_count"})
)

categories_with_max
= categories_with_lemma_counts.merge(
    max_lemma_count
,
    on
="category_name"
)

categories_mapping
= categories_with_max[
    categories_with_max
['total_orders_with_lemma'] == categories_with_max['max_lemma_count']
].groupby("category_name", as_index=False).max()
categories_mapping
.to_pandas()

Beklenen çıkış:

        category_name   total_orders    word    num_words       lemma   total_orders_with_lemma max_lemma_count
0       100 PROOF VODKA 99124   vodka   3       vodka   7575769 7575769
1       100% AGAVE TEQUILA      724374  tequila 3       tequila 1601092 1601092
2       AGED DARK RUM   59433   rum     3       rum     3226633 3226633
...     ...     ...     ...     ...     ...     ...     ...
100     WHITE CREME DE CACAO    7213    white   4       white   446225  446225
101     WHITE CREME DE MENTHE   2459    white   4       white   446225  446225
102     WHITE RUM       436553  rum     2       rum     3226633 3226633
103 rows × 7 columns

Artık her kategoriyi özetleyen tek bir lemma olduğu için bunu orijinal DataFrame ile birleştirin.

df_with_lemma = df.merge(
    categories_mapping
,
    on
="category_name",
    how
="left"
)
df_with_lemma
[df_with_lemma['category_name'].notnull()].peek()

Beklenen çıkış:

        invoice_and_item_number ...     lemma   total_orders_with_lemma max_lemma_count
0       S30989000030    ...     vodka   7575769 7575769
1       S30538800106    ...     vodka   7575769 7575769
2       S30601200013    ...     vodka   7575769 7575769
3       S30527200047    ...     vodka   7575769 7575769
4       S30833600058    ...     vodka   7575769 7575769
5 rows × 30 columns

İlçeleri karşılaştırma

Farklılıkları görmek için her ilçedeki satışları karşılaştırın.

county_lemma = (
    df_with_lemma
    .groupby(["county", "lemma"])
    .agg({"volume_sold_liters": "sum"})
    # Cast to an integer for more deterministic equality comparisons.
    .assign(volume_sold_int64=lambda _: _['volume_sold_liters'].astype("Int64"))
)

Her ilçede en çok satılan ürünü (lemma) bulun.

county_max = (
    county_lemma
   
.reset_index(drop=False)
   
.groupby("county")
   
.agg({"volume_sold_int64": "max"})
)

county_max_lemma
= county_lemma[
    county_lemma
["volume_sold_int64"] == county_max["volume_sold_int64"]
]

county_max_lemma
.to_pandas()

Beklenen çıkış:

        volume_sold_liters      volume_sold_int64
county  lemma          
SCOTT   vodka  
6044393.1       6044393
APPANOOSE       whiskey
292490.44       292490
HAMILTON        whiskey
329118.92       329118
...     ...     ...     ...
WORTH   whiskey
100542.85       100542
MITCHELL        vodka  
158791.94       158791
RINGGOLD        whiskey
65107.8 65107
101 rows × 2 columns

How different are the counties from each other?

county_max_lemma.groupby("lemma").size().to_pandas()

Beklenen çıkış:

lemma   
american        1
liqueur 1
vodka   15
whiskey 83

dtype: Int64

Çoğu eyalette hacim olarak en popüler ürün viskidir. 15 eyalette ise en popüler ürün votkadır. Bu durumu eyaletteki en popüler alkol türleriyle karşılaştırın.

total_liters = (
    df_with_lemma
   
.groupby("lemma")
   
.agg({"volume_sold_liters": "sum"})
   
.sort_values("volume_sold_liters", ascending=False)
)
total_liters
.to_pandas()

Beklenen çıkış:

        volume_sold_liters
lemma  
vodka  
85356422.950001
whiskey
85112339.980001
rum    
33891011.72
american        
19994259.64
imported        
14985636.61
tequila
12357782.37
cocktails
/rtd   7406769.87
...

Viski ve votkanın hacmi neredeyse aynıdır. Votka, eyalet genelinde viskiye göre biraz daha yüksektir.

Orantı karşılaştırması

Her ilçedeki satışlarda ne gibi benzersiz özellikler var? İlçeyi eyaletin geri kalanından farklı kılan nedir?

Eyalet genelindeki satışların oranına göre hangi alkollü içecek satış hacimlerinin oransal olarak beklenenden en çok farklı olduğunu bulmak için Cohen'in h ölçümünü kullanın.

import numpy as np

total_proportions = total_liters / total_liters.sum()
total_phi = 2 * np.arcsin(np.sqrt(total_proportions))

county_liters = df_with_lemma.groupby(["county", "lemma"]).agg({"volume_sold_liters": "sum"})
county_totals = df_with_lemma.groupby(["county"]).agg({"volume_sold_liters": "sum"})
county_proportions = county_liters / county_totals
county_phi = 2 * np.arcsin(np.sqrt(county_proportions))

cohens_h = (
   
(county_phi - total_phi)
   
.rename(columns={"volume_sold_liters": "cohens_h"})
   
.assign(cohens_h_int=lambda _: (_['cohens_h'] * 1_000_000).astype("Int64"))
)

Her bir kelime kökü için Cohen's h ölçülmüştür. Şimdi her ilçede eyalet genelindeki oranla en büyük farkı bulun.

# Note: one might want to use the absolute value here if interested in counties
# that drink _less_ of a particular liquor than expected.
largest_per_county
= cohens_h.groupby("county").agg({"cohens_h_int": "max"})
counties
= cohens_h[cohens_h['cohens_h_int'] == largest_per_county["cohens_h_int"]]
counties
.sort_values('cohens_h', ascending=False).to_pandas()

Beklenen çıkış:

        cohens_h        cohens_h_int
county  lemma          
EL PASO liqueur
1.289667        1289667
ADAMS   whiskey
0.373591        373590
IDA     whiskey
0.306481        306481
OSCEOLA whiskey
0.295524        295523
PALO ALTO       whiskey
0.293697        293696
...     ...     ...     ...
MUSCATINE       rum    
0.053757        53757
MARION  rum    
0.053427        53427
MITCHELL        vodka  
0.048212        48212
WEBSTER rum    
0.044896        44895
CERRO GORDO     cocktails
/rtd   0.027496        27495
100 rows × 2 columns

Cohen's h değeri ne kadar büyükse söz konusu alkol türünün tüketim miktarında eyalet ortalamalarına kıyasla istatistiksel olarak anlamlı bir fark olma olasılığı o kadar yüksektir. Daha küçük pozitif değerlerde, tüketimdeki fark eyalet genelindeki ortalamadan farklıdır ancak bu durum rastgele farklılıklardan kaynaklanabilir.

Not: EL PASO ilçesi Iowa'da bir ilçe gibi görünmüyor. Bu, bu sonuçlara tamamen güvenmeden önce veri temizleme işleminin yapılması gerektiğini gösterebilir.

İlçeleri görselleştirme

Her ilçenin coğrafi alanını almak için bigquery-public-data.geo_us_boundaries.counties tablosuyla birleştirin. ABD'deki ilçe adları benzersiz olmadığından yalnızca Iowa'daki ilçeleri içerecek şekilde filtreleyin. Iowa için FIPS kodu "19"dur.

counties_geo = (
    bpd.read_gbq("bigquery-public-data.geo_us_boundaries.counties")
    .assign(county=lambda _: _['county_name'].str.upper())
)
counties_plus = (
    counties
    .reset_index(drop=False)
    .merge(counties_geo[counties_geo['state_fips_code'] == '19'], on="county", how="left")
    .dropna(subset=["county_geom"])
    .to_pandas()
)
counties_plus

Beklenen çıkış:

county  lemma   cohens_h        cohens_h_int    geo_id  state_fips_code ...
0       ALLAMAKEE       american        0.087931        87930   19005   19      ...
1       BLACK HAWK      american        0.106256        106256  19013   19      ...
2       WINNESHIEK      american        0.093101        93101   19191   19      ...
...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...     ...
96      CLINTON tequila 0.075708        75707   19045   19      ...
97      POLK    tequila 0.087438        87438   19153   19      ...
98      LEE     schnapps        0.064663        64663   19111   19      ...
99 rows × 23 columns

Bu farklılıkları bir haritada görselleştirmek için GeoPandas'ı kullanın.

import geopandas

counties_plus = geopandas.GeoDataFrame(counties_plus, geometry="county_geom")

# https://stackoverflow.com/a/42214156/101923
ax = counties_plus.plot(figsize=(14, 14))
counties_plus.apply(
   
lambda row: ax.annotate(
       
text=row['lemma'],
       
xy=row['county_geom'].centroid.coords[0],
       
ha='center'
   
),
   
axis=1,
)

Her ilçedeki eyalet genelindeki satış hacmi oranlarından en farklı olan alkolün haritası

9. Temizleme

Bu eğitim için yeni bir Google Cloud projesi oluşturduysanız oluşturulan tablolar veya diğer kaynaklar için ek ücret alınmaması amacıyla projeyi silebilirsiniz.

Alternatif olarak, bu eğitim için oluşturulan Cloud Functions'ları, hizmet hesaplarını ve veri kümelerini silin.

10. Tebrikler!

BigQuery veri çerçevelerini kullanarak yapılandırılmış verileri temizlediniz ve analiz ettiniz. Bu süreçte Google Cloud'un herkese açık veri kümelerini, BigQuery Studio'daki Python not defterlerini, BigQuery ML'yi, BigQuery uzak işlevlerini ve BigQuery veri çerçevelerinin gücünü keşfettiniz. Tebrikler!

Sonraki adımlar