BigQuery DataFrames প্যাকেজ ব্যবহার করে আইওয়া মদের বিক্রয়ের অনুসন্ধানমূলক ডেটা বিশ্লেষণ

1. ওভারভিউ

এই ল্যাবে, আপনি আইওয়া মদের বিক্রয় পাবলিক ডেটাসেট পরিষ্কার এবং বিশ্লেষণ করতে BigQuery স্টুডিওতে পাইথন নোটবুক থেকে BigQuery ডেটাফ্রেম ব্যবহার করবেন। অন্তর্দৃষ্টি আবিষ্কার করতে BigQuery ML এবং দূরবর্তী ফাংশন ক্ষমতা ব্যবহার করুন।

ভৌগলিক এলাকায় বিক্রির তুলনা করার জন্য আপনি একটি পাইথন নোটবুক তৈরি করবেন। এটি যেকোন স্ট্রাকচার্ড ডেটাতে কাজ করার জন্য মানিয়ে নেওয়া যেতে পারে।

উদ্দেশ্য

এই ল্যাবে, আপনি কীভাবে নিম্নলিখিত কাজগুলি সম্পাদন করবেন তা শিখবেন:

  • BigQuery স্টুডিওতে পাইথন নোটবুক সক্রিয় করুন এবং ব্যবহার করুন
  • BigQuery ডেটাফ্রেম প্যাকেজ ব্যবহার করে BigQuery-এ সংযোগ করুন
  • BigQuery ML ব্যবহার করে একটি লিনিয়ার রিগ্রেশন তৈরি করুন
  • একটি পরিচিত পান্ডা-সদৃশ সিনট্যাক্স ব্যবহার করে জটিল সমষ্টি এবং যোগদান করুন

2. প্রয়োজনীয়তা

আপনি শুরু করার আগে

এই কোডল্যাবের নির্দেশাবলী অনুসরণ করতে, আপনার BigQuery স্টুডিও সক্ষম সহ একটি Google ক্লাউড প্রকল্প এবং একটি সংযুক্ত বিলিং অ্যাকাউন্টের প্রয়োজন হবে৷

  1. Google ক্লাউড কনসোলে , প্রকল্প নির্বাচক পৃষ্ঠায়, একটি Google ক্লাউড প্রকল্প নির্বাচন করুন বা তৈরি করুন৷
  2. আপনার Google ক্লাউড প্রকল্পের জন্য বিলিং সক্ষম করা আছে তা নিশ্চিত করুন৷ একটি প্রকল্পে বিলিং সক্ষম কিনা তা পরীক্ষা করতে শিখুন
  3. সম্পদ ব্যবস্থাপনার জন্য BigQuery স্টুডিও সক্ষম করতে নির্দেশাবলী অনুসরণ করুন।

BigQuery স্টুডিও প্রস্তুত করুন

একটি খালি নোটবুক তৈরি করুন এবং এটি একটি রানটাইমের সাথে সংযুক্ত করুন।

  1. Google ক্লাউড কনসোলে BigQuery স্টুডিওতে যান।
  2. + বোতামের পাশে ক্লিক করুন।
  3. পাইথন নোটবুক নির্বাচন করুন।
  4. টেমপ্লেট নির্বাচক বন্ধ করুন।
  5. একটি নতুন কোড সেল তৈরি করতে + কোড নির্বাচন করুন।
  6. কোড সেল থেকে BigQuery DataFrames প্যাকেজের সর্বশেষ সংস্করণটি ইনস্টল করুন৷ নিম্নলিখিত কমান্ডটি টাইপ করুন৷
    %pip install --upgrade bigframes --quiet
    
    চালান সেল বোতামে ক্লিক করুন বা কোড সেল চালাতে Shift + Enter টিপুন।

3. একটি পাবলিক ডেটাসেট পড়ুন

একটি নতুন কোড সেলে নিম্নলিখিতগুলি চালিয়ে BigQuery ডেটাফ্রেম প্যাকেজটি শুরু করুন:

import bigframes.pandas as bpd

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

দ্রষ্টব্য: এই টিউটোরিয়ালে, আমরা পরীক্ষামূলক "আংশিক অর্ডারিং মোড" ব্যবহার করি, যা পান্ডা-সদৃশ ফিল্টারিংয়ের সাথে ব্যবহার করার সময় আরও দক্ষ প্রশ্নের জন্য অনুমতি দেয়। কিছু পান্ডা বৈশিষ্ট্য যার জন্য কঠোর অর্ডার বা সূচী প্রয়োজন তা কাজ নাও করতে পারে।

সঙ্গে আপনার bigframes প্যাকেজ সংস্করণ পরীক্ষা করুন

bpd.__version__

এই টিউটোরিয়ালটির জন্য 1.27.0 বা পরবর্তী সংস্করণ প্রয়োজন।

আইওয়া মদ খুচরা বিক্রয়

Google ক্লাউডের পাবলিক ডেটাসেট প্রোগ্রামের মাধ্যমে BigQuery-এ আইওয়া মদের খুচরা বিক্রয় ডেটাসেট প্রদান করা হয়। এই ডেটাসেটে আইওয়া রাজ্যে 1 জানুয়ারী, 2012 থেকে ব্যক্তিদের কাছে বিক্রয়ের জন্য খুচরা বিক্রেতাদের দ্বারা মদের প্রতিটি পাইকারি ক্রয় রয়েছে। ডেটা আইওয়া বাণিজ্য বিভাগের মধ্যে অ্যালকোহলিক বেভারেজ বিভাগ দ্বারা সংগ্রহ করা হয়।

BigQuery-এ, Iowa মদের খুচরা বিক্রয় বিশ্লেষণ করতে bigquery-public-data.iowa_liquor_sales.sales-এ প্রশ্ন করুন। একটি ক্যোয়ারী স্ট্রিং বা টেবিল আইডি থেকে একটি ডেটাফ্রেম তৈরি করতে bigframes.pandas.read_gbq() পদ্ধতি ব্যবহার করুন।

"df" নামে একটি ডেটাফ্রেম তৈরি করতে একটি নতুন কোড সেলে নিম্নলিখিতটি চালান:

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

একটি ডেটাফ্রেম সম্পর্কে প্রাথমিক তথ্য আবিষ্কার করুন

ডেটার একটি ছোট নমুনা ডাউনলোড করতে DataFrame.peek() পদ্ধতি ব্যবহার করুন।

এই সেল চালান:

df.peek()

প্রত্যাশিত আউটপুট:

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	...				

দ্রষ্টব্য: head() অর্ডার করার প্রয়োজন হয় এবং আপনি যদি ডেটার একটি নমুনা কল্পনা করতে চান তবে peek() এর চেয়ে কম দক্ষ।

পান্ডাগুলির মতোই, সমস্ত উপলব্ধ কলাম এবং তাদের সংশ্লিষ্ট ডেটা প্রকারগুলি দেখতে DataFrame.dtypes বৈশিষ্ট্য ব্যবহার করুন৷ এগুলি পান্ডা-সামঞ্জস্যপূর্ণ উপায়ে প্রকাশ করা হয়।

এই সেল চালান:

df.dtypes

প্রত্যাশিত আউটপুট:

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() পদ্ধতিটি DataFrame থেকে কিছু মৌলিক পরিসংখ্যান জিজ্ঞাসা করে। একটি পান্ডাস ডেটাফ্রেম হিসাবে এই সারাংশ পরিসংখ্যানগুলি ডাউনলোড করতে DataFrame.to_pandas() চালান৷

এই সেল চালান:

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

প্রত্যাশিত আউটপুট:

	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. ভিজ্যুয়ালাইজ করুন এবং ডেটা পরিষ্কার করুন

আইওয়া মদের খুচরা বিক্রয় ডেটাসেট খুচরো দোকানগুলি কোথায় অবস্থিত তা সহ সূক্ষ্ম-দানাযুক্ত ভৌগলিক তথ্য সরবরাহ করে। ভৌগলিক এলাকায় প্রবণতা এবং পার্থক্য সনাক্ত করতে এই ডেটা ব্যবহার করুন।

পিন কোড প্রতি বিক্রয় কল্পনা করুন

DataFrame.plot.hist() এর মতো বেশ কয়েকটি অন্তর্নির্মিত ভিজ্যুয়ালাইজেশন পদ্ধতি রয়েছে। জিপ কোড দ্বারা মদ বিক্রয় তুলনা করতে এই পদ্ধতি ব্যবহার করুন.

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

প্রত্যাশিত আউটপুট:

ভলিউমের হিস্টোগ্রাম

কোন জিপ কোল্ডে সবচেয়ে বেশি অ্যালকোহল বিক্রি হয়েছে তা দেখতে একটি বার চার্ট ব্যবহার করুন।

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

প্রত্যাশিত আউটপুট:

শীর্ষ বিক্রি হওয়া জিপ কোডগুলিতে অ্যালকোহলের পরিমাণের বার চার্ট

ডেটা পরিষ্কার করুন

কিছু জিপ কোডের ট্রেইলিং আছে .0 । সম্ভবত ডেটা সংগ্রহের কোথাও জিপ কোডগুলি দুর্ঘটনাক্রমে ফ্লোটিং পয়েন্ট মানগুলিতে রূপান্তরিত হয়েছিল। জিপ কোড পরিষ্কার করতে নিয়মিত এক্সপ্রেশন ব্যবহার করুন এবং বিশ্লেষণ পুনরাবৃত্তি করুন।

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

প্রত্যাশিত আউটপুট:

শীর্ষ বিক্রি হওয়া জিপ কোডগুলিতে অ্যালকোহলের পরিমাণের বার চার্ট

5. বিক্রয়ের মধ্যে পারস্পরিক সম্পর্ক আবিষ্কার করুন

কেন কিছু জিপ কোড অন্যদের চেয়ে বেশি বিক্রি করে? একটি অনুমান হল যে এটি জনসংখ্যার আকারের পার্থক্যের কারণে। বেশি জনসংখ্যা সহ একটি জিপ কোড সম্ভবত আরও মদ বিক্রি করবে।

জনসংখ্যা এবং মদ বিক্রির পরিমাণের মধ্যে পারস্পরিক সম্পর্ক গণনা করে এই অনুমান পরীক্ষা করুন।

অন্যান্য ডেটাসেটের সাথে যোগ দিন

ইউএস সেন্সাস ব্যুরোর আমেরিকান কমিউনিটি সার্ভে জিপ কোড ট্যাবুলেশন এলাকা সমীক্ষার মতো জনসংখ্যা ডেটাসেটের সাথে যোগ দিন।

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

আমেরিকান কমিউনিটি সার্ভে জিওআইডি দ্বারা রাজ্যগুলিকে চিহ্নিত করে৷ জিপ কোড ট্যাবুলেশন এলাকার ক্ষেত্রে, জিওআইডি জিপ কোডের সমান।

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

লিটার অ্যালকোহল বিক্রির সাথে জিপ কোড ট্যাবুলেশন এলাকার জনসংখ্যার তুলনা করার জন্য একটি স্ক্যাটার প্লট তৈরি করুন।

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

প্রত্যাশিত আউটপুট:

জনসংখ্যা এবং লিটার বিক্রি মদ দ্বারা জিপ কোড ট্যাবুলেশন এলাকায় ছড়িয়ে ছিটিয়ে প্লট

পারস্পরিক সম্পর্ক গণনা করুন

প্রবণতা মোটামুটি রৈখিক দেখায়. জনসংখ্যা কতটা ভালোভাবে মদ বিক্রির পূর্বাভাস দিতে পারে তা পরীক্ষা করার জন্য এটিতে একটি লিনিয়ার রিগ্রেশন মডেল ফিট করুন।

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 পদ্ধতি ব্যবহার করে ফিট কতটা ভাল তা পরীক্ষা করুন।

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

নমুনা আউটপুট:

	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

জনসংখ্যার মানগুলির একটি পরিসরে predict ফাংশনকে কল করে সেরা ফিট লাইন আঁকুন।

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",
)

প্রত্যাশিত আউটপুট:

একটি সর্বোত্তম ফিট লাইন সহ স্ক্যাটার প্লট

heteroscedasticity সম্বোধন

পূর্ববর্তী চার্টের ডেটা হেটেরোসেডেস্টিক বলে মনে হচ্ছে। সেরা ফিট লাইনের চারপাশের বৈচিত্র্য জনসংখ্যার সাথে বৃদ্ধি পায়।

সম্ভবত প্রতি ব্যক্তির কেনা অ্যালকোহলের পরিমাণ তুলনামূলকভাবে ধ্রুবক।

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

প্রত্যাশিত আউটপুট:

জনসংখ্যা প্রতি লিটার ছত্রাক প্লট

দুটি ভিন্ন উপায়ে কেনা গড় লিটার অ্যালকোহল গণনা করুন:

  1. আইওয়াতে একজন ব্যক্তি প্রতি কেনা অ্যালকোহলের গড় পরিমাণ কত?
  2. জনপ্রতি কেনা অ্যালকোহলের পরিমাণের সমস্ত জিপ কোডের গড় কত।

(1), এটি প্রতিফলিত করে যে সমগ্র রাজ্যে কতটা অ্যালকোহল কেনা হয়। (2) তে, এটি গড় জিপ কোড প্রতিফলিত করে, যা অগত্যা (1) এর মতো হবে না কারণ বিভিন্ন জিপ কোডের বিভিন্ন জনসংখ্যা রয়েছে।

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

প্রত্যাশিত আউটপুট: 87.997

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

প্রত্যাশিত আউটপুট: 67.139

উপরের মত এই গড় প্লট করুন।

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

প্রত্যাশিত আউটপুট:

জনসংখ্যা প্রতি লিটার ছত্রাক প্লট

এখনও কিছু জিপ কোড আছে যেগুলো বেশ বড় আউটলার, বিশেষ করে কম জনসংখ্যার এলাকায়। এটি কেন এটি অনুমান করার অনুশীলন হিসাবে রেখে দেওয়া হয়েছে। উদাহরণস্বরূপ, এটি হতে পারে যে কিছু জিপ কোড কম জনসংখ্যার কিন্তু উচ্চ খরচ কারণ তারা এলাকায় একমাত্র মদের দোকান ধারণ করে। যদি তাই হয়, আশেপাশের জিপ কোডের জনসংখ্যার ভিত্তিতে গণনা করা হয় তবে এই আউটলায়ারগুলিও বের হয়ে যেতে পারে।

6. বিক্রিত মদের প্রকারের তুলনা করা

ভৌগলিক তথ্য ছাড়াও, আইওয়া মদের খুচরা বিক্রয় ডাটাবেসে বিক্রি হওয়া আইটেম সম্পর্কে বিস্তারিত তথ্য রয়েছে। সম্ভবত এগুলি বিশ্লেষণ করে, আমরা ভৌগলিক অঞ্চল জুড়ে স্বাদের পার্থক্য প্রকাশ করতে পারি।

বিভাগগুলি অন্বেষণ করুন

আইটেমগুলি ডাটাবেসে শ্রেণীবদ্ধ করা হয়। কয়টি বিভাগ আছে?

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

প্রত্যাশিত আউটপুট: 103

ভলিউম অনুযায়ী সবচেয়ে জনপ্রিয় বিভাগ কোনটি?

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)

বিক্রি হওয়া মদের শীর্ষ শ্রেণীর বার চার্ট

ARRAY ডেটা টাইপের সাথে কাজ করা

হুইস্কি, রাম, ভদকা এবং আরও অনেক কিছুর বিভিন্ন বিভাগ রয়েছে। আমি এই একত্রে একত্রিত করতে চাই.

Series.str.split() পদ্ধতি ব্যবহার করে বিভাগের নামগুলিকে পৃথক শব্দে বিভক্ত করে শুরু করুন। explode() পদ্ধতি ব্যবহার করে এটি তৈরি করা অ্যারেটিকে আনস্ট করুন।

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)

বিভাগ থেকে গণনা দ্বারা শব্দ

category_parts.nunique()

প্রত্যাশিত আউটপুট: 113

উপরের চার্টের দিকে তাকালে, ডেটাতে এখনও VODKAS থেকে VODKA আলাদা রয়েছে৷ একটি ছোট সেটে বিভাগগুলিকে ভেঙে ফেলার জন্য আরও গ্রুপিং প্রয়োজন৷

7. BigQuery ডেটাফ্রেমের সাথে NLTK ব্যবহার করা

মাত্র 100টি বিভাগের সাথে, কিছু হিউরিস্টিক লেখা বা এমনকি ম্যানুয়ালি ক্যাটাগরি থেকে বৃহত্তর মদের ধরন পর্যন্ত একটি ম্যাপিং তৈরি করা সম্ভব হবে। বিকল্পভাবে, এই ধরনের একটি ম্যাপিং তৈরি করতে কেউ একটি বড় ভাষার মডেল যেমন জেমিনি ব্যবহার করতে পারে। কোডল্যাব ব্যবহার করে দেখুন Gemini-এর সাথে BigQuery DataFrames ব্যবহার করতে BigQuery ডেটাফ্রেম ব্যবহার করে অসংগঠিত ডেটা থেকে অন্তর্দৃষ্টি পান

পরিবর্তে, এই ডেটাগুলি প্রক্রিয়া করতে আরও ঐতিহ্যগত প্রাকৃতিক ভাষা প্রক্রিয়াকরণ প্যাকেজ, NLTK ব্যবহার করুন। একটি "স্টেমার" নামক প্রযুক্তি বহুবচন এবং একবচন বিশেষ্যকে একই মানের মধ্যে একত্রিত করতে পারে, উদাহরণস্বরূপ।

NLTK ব্যবহার করে শব্দগুলো আটকানো

NLTK প্যাকেজ প্রাকৃতিক ভাষা প্রক্রিয়াকরণ পদ্ধতি প্রদান করে যা পাইথন থেকে অ্যাক্সেসযোগ্য। এটি চেষ্টা করার জন্য প্যাকেজ ইনস্টল করুন.

%pip install nltk

পরবর্তী, প্যাকেজ আমদানি করুন. সংস্করণ পরিদর্শন করুন. এটি টিউটোরিয়ালে পরে ব্যবহার করা হবে।

import nltk

nltk.__version__

শব্দটিকে "স্টেম" করার জন্য শব্দের মানসম্মত করার একটি উপায়। এটি বহুবচনের জন্য একটি অনুগামী "s" হিসাবে যেকোনো প্রত্যয়কে সরিয়ে দেয়।

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)

কয়েকটি শব্দে এটি চেষ্টা করুন।

stem("WHISKEY")

প্রত্যাশিত আউটপুট: whiskey

stem("WHISKIES")

প্রত্যাশিত আউটপুট: whiski

দুর্ভাগ্যবশত, এটি হুইস্কিকে হুইস্কির মতো ম্যাপ করেনি। স্টেমারগুলি অনিয়মিত বহুবচনের সাথে ভাল কাজ করে না। একটি লেমাটাইজার ব্যবহার করে দেখুন, যা বেস শব্দ সনাক্ত করতে আরও পরিশীলিত কৌশল ব্যবহার করে, যাকে "লেমা" বলা হয়।

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

কয়েকটি শব্দে এটি চেষ্টা করুন।

lemmatize("WHISKIES")

প্রত্যাশিত আউটপুট: whisky

lemmatize("WHISKY")

প্রত্যাশিত আউটপুট: whisky

lemmatize("WHISKEY")

প্রত্যাশিত আউটপুট: whiskey

দুর্ভাগ্যবশত, এই লেমাটাইজার "হুইস্কি" কে "হুইস্কি" এর মতো একই লেমার সাথে ম্যাপ করে না। যেহেতু এই শব্দটি আইওয়া খুচরা মদের বিক্রয় ডাটাবেসের জন্য বিশেষভাবে গুরুত্বপূর্ণ, তাই একটি অভিধান ব্যবহার করে ম্যানুয়ালি আমেরিকান বানানে ম্যাপ করুন।

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)

কয়েকটি শব্দে এটি চেষ্টা করুন।

lemmatize("WHISKIES")

প্রত্যাশিত আউটপুট: whiskey

lemmatize("WHISKEY")

প্রত্যাশিত আউটপুট: whiskey

অভিনন্দন! এই লেমেটাইজারটি বিভাগগুলিকে সংকীর্ণ করার জন্য ভাল কাজ করা উচিত। BigQuery এর সাথে এটি ব্যবহার করতে, আপনাকে অবশ্যই এটিকে ক্লাউডে স্থাপন করতে হবে।

ফাংশন স্থাপনার জন্য আপনার প্রকল্প সেটআপ করুন

আপনি এটিকে ক্লাউডে স্থাপন করার আগে যাতে BigQuery এই ফাংশনটি অ্যাক্সেস করতে পারে, আপনাকে কিছু এককালীন সেটআপ করতে হবে।

একটি নতুন কোড সেল তৈরি করুন এবং এই টিউটোরিয়ালের জন্য আপনি যে Google ক্লাউড প্রকল্প আইডি ব্যবহার করছেন তার সাথে your-project-id প্রতিস্থাপন করুন।

project_id = "your-project-id"

কোনও অনুমতি ছাড়াই একটি পরিষেবা অ্যাকাউন্ট তৈরি করুন, যেহেতু এই ফাংশনের কোনও ক্লাউড সংস্থানগুলিতে অ্যাক্সেসের প্রয়োজন নেই৷

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)

প্রত্যাশিত আউটপুট: bigframes-no-permissions@your-project-id.iam.gserviceaccount.com

ফাংশন ধরে রাখতে একটি BigQuery ডেটাসেট তৈরি করুন।

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)

একটি দূরবর্তী ফাংশন স্থাপন করা হচ্ছে

ক্লাউড ফাংশন এপিআই সক্ষম করুন যদি এখনও সক্ষম না থাকে।

!gcloud services enable cloudfunctions.googleapis.com

এখন, আপনার তৈরি করা ডেটাসেটে আপনার ফাংশন স্থাপন করুন। পূর্ববর্তী ধাপে আপনি যে ফাংশনটি তৈরি করেছেন তাতে একটি @bpd.remote_function ডেকোরেটর যোগ করুন।

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

স্থাপনা প্রায় দুই মিনিট সময় নিতে হবে.

দূরবর্তী ফাংশন ব্যবহার করে

একবার স্থাপনা সম্পূর্ণ হলে, আপনি এই ফাংশনটি পরীক্ষা করতে পারেন।

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

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

প্রত্যাশিত আউটপুট:

0	whiskey
1	whiskey
2	whiskey
3	vodka
4	vodka

dtype: string

8. কাউন্টি দ্বারা অ্যালকোহল খরচ তুলনা

এখন যেহেতু lemmatize ফাংশন উপলব্ধ, বিভাগগুলি একত্রিত করতে এটি ব্যবহার করুন।

শ্রেনীটি সর্বোত্তম সংক্ষিপ্ত করার জন্য শব্দটি সন্ধান করা

প্রথমে, ডাটাবেসের সমস্ত বিভাগের একটি ডেটাফ্রেম তৈরি করুন।

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

প্রত্যাশিত আউটপুট:

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

এর পরে, বিরাম চিহ্ন এবং "আইটেম" এর মতো কয়েকটি ফিলার শব্দ ব্যতীত বিভাগগুলিতে সমস্ত শব্দের একটি ডেটাফ্রেম তৈরি করুন৷

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

প্রত্যাশিত আউটপুট:

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

মনে রাখবেন যে গ্রুপিং করার পরে লেমেটাইজিং করে, আপনি আপনার ক্লাউড ফাংশনের লোড কমিয়ে দিচ্ছেন। ডাটাবেসের কয়েক মিলিয়ন সারির প্রতিটিতে লেমমাটাইজ ফাংশন প্রয়োগ করা সম্ভব, তবে গ্রুপ করার পরে এটি প্রয়োগ করার চেয়ে বেশি খরচ হবে এবং কোটা বৃদ্ধির প্রয়োজন হতে পারে।

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

প্রত্যাশিত আউটপুট:

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

এখন যেহেতু শব্দগুলি লেমাটাইজ করা হয়েছে, আপনাকে সেই লেমা নির্বাচন করতে হবে যা শ্রেনীটিকে সর্বোত্তম সংক্ষিপ্ত করে। যেহেতু বিভাগগুলিতে অনেকগুলি ফাংশন শব্দ নেই, তাই হিউরিস্টিক ব্যবহার করুন যে যদি একটি শব্দ একাধিক অন্যান্য বিভাগে উপস্থিত হয় তবে এটি সম্ভবত একটি সংক্ষিপ্ত শব্দ (যেমন হুইস্কি) হিসাবে ভাল।

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

প্রত্যাশিত আউটপুট:

	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

এখন যেহেতু প্রতিটি বিভাগের সারসংক্ষেপ একটি একক লেমা আছে, এটিকে মূল ডেটাফ্রেমে মার্জ করুন।

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

প্রত্যাশিত আউটপুট:

	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

কাউন্টি তুলনা

কি পার্থক্য আছে তা দেখতে প্রতিটি কাউন্টিতে বিক্রয় তুলনা করুন।

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

প্রতিটি কাউন্টিতে সর্বাধিক বিক্রিত পণ্য (লেমা) খুঁজুন।

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

প্রত্যাশিত আউটপুট:

	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

কাউন্টিগুলো একে অপরের থেকে কতটা আলাদা?

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

প্রত্যাশিত আউটপুট:

lemma	
american	1
liqueur	1
vodka	15
whiskey	83

dtype: Int64

বেশিরভাগ কাউন্টিতে, হুইস্কি হল ভলিউম অনুসারে সবচেয়ে জনপ্রিয় পণ্য, 15টি কাউন্টিতে ভদকা সবচেয়ে জনপ্রিয়। রাজ্যব্যাপী সর্বাধিক জনপ্রিয় মদের প্রকারের সাথে এটি তুলনা করুন।

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

প্রত্যাশিত আউটপুট:

	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
...

হুইস্কি এবং ভদকার ভলিউম প্রায় একই, রাজ্যব্যাপী হুইস্কির চেয়ে ভদকা কিছুটা বেশি।

অনুপাত তুলনা

প্রতিটি কাউন্টি বিক্রয় সম্পর্কে অনন্য কি? কি রাজ্যের বাকি থেকে কাউন্টি আলাদা করে তোলে?

রাজ্যব্যাপী বিক্রয়ের অনুপাতের উপর ভিত্তি করে যা আশা করা হবে তার থেকে কোন মদের বিক্রির পরিমাণ সবচেয়ে বেশি আনুপাতিকভাবে আলাদা তা খুঁজে বের করতে কোহেনের h পরিমাপ ব্যবহার করুন।

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

এখন যেহেতু প্রতিটি লেমার জন্য কোহেনের h পরিমাপ করা হয়েছে, প্রতিটি কাউন্টিতে রাজ্যব্যাপী অনুপাত থেকে সবচেয়ে বড় পার্থক্য খুঁজুন।

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

প্রত্যাশিত আউটপুট:

	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

কোহেনের এইচ মান যত বড় হবে, রাষ্ট্রীয় গড়গুলির তুলনায় এই ধরনের অ্যালকোহল গ্রহণের পরিমাণে পরিসংখ্যানগতভাবে উল্লেখযোগ্য পার্থক্য হওয়ার সম্ভাবনা তত বেশি। ছোট ইতিবাচক মানগুলির জন্য, খরচের পার্থক্য রাজ্যব্যাপী গড়ের চেয়ে আলাদা, তবে এটি এলোমেলো পার্থক্যের কারণে হতে পারে।

একটি বাদ দিয়ে: EL PASO কাউন্টি আইওয়াতে একটি কাউন্টি বলে মনে হচ্ছে না এটি এই ফলাফলগুলির উপর সম্পূর্ণ নির্ভর করার আগে ডেটা পরিষ্কারের জন্য আরেকটি প্রয়োজন নির্দেশ করতে পারে।

ভিজ্যুয়ালাইজিং কাউন্টি

প্রতিটি কাউন্টির ভৌগলিক এলাকা পেতে bigquery-public-data.geo_us_boundaries.counties টেবিলের সাথে যোগ দিন। কাউন্টির নামগুলি মার্কিন যুক্তরাষ্ট্র জুড়ে অনন্য নয়, তাই শুধুমাত্র আইওয়া থেকে কাউন্টিগুলি অন্তর্ভুক্ত করতে ফিল্টার করুন৷ আইওয়ার জন্য FIPS কোড হল '19'।

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

প্রত্যাশিত আউটপুট:

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

একটি মানচিত্রে এই পার্থক্যগুলি কল্পনা করতে GeoPandas ব্যবহার করুন৷

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

অ্যালকোহলের একটি মানচিত্র যা প্রতিটি কাউন্টিতে রাজ্যব্যাপী বিক্রির পরিমাণের অনুপাত থেকে সবচেয়ে আলাদা

9. পরিষ্কার করুন

আপনি যদি এই টিউটোরিয়ালের জন্য একটি নতুন Google ক্লাউড প্রকল্প তৈরি করে থাকেন, তাহলে টেবিল বা অন্যান্য সংস্থানগুলির জন্য অতিরিক্ত চার্জ প্রতিরোধ করতে আপনি এটি মুছে ফেলতে পারেন।

বিকল্পভাবে, এই টিউটোরিয়ালের জন্য তৈরি করা ক্লাউড ফাংশন, পরিষেবা অ্যাকাউন্ট এবং ডেটাসেটগুলি মুছুন।

10. অভিনন্দন!

আপনি BigQuery DataFrames ব্যবহার করে স্ট্রাকচার্ড ডেটা পরিষ্কার ও বিশ্লেষণ করেছেন। পথ ধরে আপনি Google ক্লাউডের পাবলিক ডেটাসেট, BigQuery স্টুডিওতে পাইথন নোটবুক, BigQuery ML, BigQuery রিমোট ফাংশন এবং BigQuery ডেটাফ্রেমের ক্ষমতা অন্বেষণ করেছেন। চমত্কার কাজ!

পরবর্তী পদক্ষেপ