หลังจากจบบทเรียน python ของ Data science bootcamp#11 ของแอดทอยแล้ว ในบทเรียนมีสอนหลายอย่างครับ ไม่ว่าจะเป็น …
- Intro to Python
- Essential Python for Data Science
- Intermediate Pandas I
- Intermediate Pandas II
- Intro to Web Scraping
สุดท้ายก็มีการบ้านให้มาทบทวนความรู้ที่ได้ เป็นยังไงไปดูกัน
Link Code เต็มๆ หนุ่ม Public ไว้ที่ DataLore นะค้าบ https://datalore.jetbrains.com/report/static/oyGEVJti8hBTc2Odm6CL2n/ckOM52Ffha742xL1B94S8o
Link data source: https://drive.google.com/file/d/1bBu6Hpf4DID06cR1ktG-hS32WkkClv-U/view?usp=sharing
อธิบายโค้ดได้ประมาณนี้ค้าบ 😎
import pandas as pd
pd.options.display.max_columns = None
import warnings
warnings.filterwarnings("ignore")
import numpy as np
df = pd.read_csv("Ref/Data Files/sample-store.csv")
print(df.shape )
df.head()
หลังจากที่ได้ import library pandas, numpy และ import sample-store.csv เข้าสู่ dataframe แล้วมาตรวจสอบ data type ของแต่ละคอลัมน์ก่อน
# see data frame information using .info()
df.info()

ข้อมูลส่วนใหญ่ pandas มองว่าเป็น object หรือ string จึงต้องมีการ convert data type ก่อน ก็จะมีคอลัมน์ order date, ship date ที่ต้องแปลงเป็น datetime
# use pd.to_datetime() function to convert columns 'Order Date' and 'Ship Date' to datetime.
df['Order Date'] = pd.to_datetime(df['Order Date'], format='%m/%d/%Y')
df['Ship Date'] = pd.to_datetime(df['Ship Date'], format='%m/%d/%Y')

คำถาม 1 – convert order date and ship date to datetime in the original dataframe เราสามารถใช้ method .dtype ในการตรวจสอบ data type เฉพาะคอลัมน์ของ df ได้
# convert order date and ship date to datetime in the original dataframe
print(f"The data type of 'Order Date' is {df['Order Date'].dtype}.")
print(f"The data type of 'Ship Date' is {df['Ship Date'].dtype}.")

คำถาม 2 – count NaN in postal code column โดยที่ NaN (Not a Number): represents missing or undefined data in Python. หรือที่เราเข้าใจว่าค่าว่างหรือ null นั่นแหละ
# count NaN in postal code column
nan_postal = df['Postal Code'].isna().sum()
print(f"The number of NaN in postal code column is {nan_postal} records")

คำถาม 3 – filter rows with missing values ก็คือค่า NaN เหมือนข้อที่ผ่านมา แต่เพื่อความชัวร์ สามารถเช็คได้ทุกคอลัมน์ใน df เลยโดยใช้ .isna().sum() ต่อท้าย df
df.isna().sum()

จะเห็นว่ามีคอลัมน์เดียวที่มี NaN อยู่คือ Postal Code จึงทำการ filter ข้อมูลนั้นออกมา
# filter rows with missing values
miss_val = df[ df['Postal Code'].isna() ]
print(f"the number of rows with missing values are {miss_val.shape[0]} rows.")
miss_val.sample(3)

คำถาม 4 – How many records are grouped by Category and Sub-Category? เป็นข้อที่ต้องการทำการกรุ๊ปรวม 2 เลเยอร์ คือ Category และ Sub-Category จากนั้นใช้ .agg() เพื่อนับจำนวน order ออกมา จากนั้นทำการเรียงจากมากไปน้อยในกลุ่มCategory
# How many records are grouped by Category and Sub-Category?
grouped_df = df.groupby(["Category", "Sub-Category"])['Row ID'].agg(['count']).reset_index()
grouped_df.sort_values(['Category', 'count'], ascending=[True, False])

คำถาม 5 – how many columns, rows in this dataset เป็นการถามหาจำนวน observations และ columns ทั้งหมดใน df นี้ สามารถใช้ method .shape ในการตรวจสอบได่เช่นกัน
# how many columns, rows in this dataset
print(f"the number of rows are {df.shape[0]} rows.")
print(f"the number of columns are {df.shape[1]} columns.")

คำถาม 6 – is there any missing values?, if there is, which colunm? how many nan values? เป็น 3 คำถามย่อยเลยทีเดียว ซึ่งสามารถใช้ .isna().sum() เพื่อหาจำนวนค่าว่างในแต่ละคอลัมน์ก่อน -> สร้างเป็น data frame -> แล้วค่อยใช้ .query() เพื่อกรองออกมาเฉพาะ row ที่มากกว่า 0
# is there any missing values?, if there is, which colunm? how many nan values?
miss_df = df.isna().sum()
miss_df = pd.DataFrame(miss_df).reset_index()
miss_df.columns = ['index', 'count_nan']
miss_df.query('count_nan > 0')
วิธีนี้จะช่วยแสดงผลคอลัมน์อื่นๆ ด้วยหากมีค่าว่างเกิดขึ้น ไม่ได้เจาะจงเฉพาะคอลัมน์ๆ ไป

คำถาม 7 – your friend ask for ‘California’ data, filter it and export csv for him ต้องการข้อมูลโดยมีเงื่อนไขเดียว และใช้ .to_csv() เพื่อ export csv file ออกมา
# your friend ask for `California` data, filter it and export csv for him
ca_df = df[df['State'] == 'California']
ca_df.to_csv("ExportFiles/california_df.csv")

คำถาม 8 – your friend ask for all order data in ‘California’ and ‘Texas’ in 2017 (look at Order Date), send him csv file เป็นคำถามที่ต้องเพิ่มเงื่อนไขเข้าไปอีก 2 ตัว พิเศษคือเราสามารถใช้ .dt.year เพื่อ convert คอลัมน์ที่เป็น datetime อยู่แล้ว ออกมาเฉพาะปี (yyyy) ได้
# your friend ask for all order data in 'California' and 'Texas' in 2017 (look at Order Date), send him csv file
ca_tx_df = df[ ((df['State'] == 'California') | (df['State'] == 'Texas')) & (df['Order Date'].dt.year == 2017)]
ca_tx_df.to_csv("ExportFiles/california_texas_2017_df.csv")

คำถาม 9 – how much total sales, average sales, and standard deviation of sales your company make in 2017 เป็นคำถามที่ต้องการค่า aggregate ถึง 3 ตัวได้แก่ sum, average และ standard deviation
จึงทำการกรอง df ออกมาเฉพาะปี 2017 ก่อน แล้วเพิ่มคอลัมน์ใหม่ที่เป็น ปี (yyyy) เข้าไป จากนั้นทำการ groupby() และ agg() จึงได้ผลลัพธ์ออกมา
# how much total sales, average sales, and standard deviation of sales your company make in 2017
df_2017 = df[df['Order Date'].dt.year == 2017]
df_2017['year'] = df['Order Date'].dt.year
df_2017.groupby('year')['Sales'].agg(['sum', 'mean', 'std']).reset_index()

วิธีนี้หากเราไม่กรองเฉพาะปี 2017 ออกมา มันจะแสดงผลปีอื่นๆ ที่มีข้อมูลด้วย ดังรูป

คำถาม 10 – which Segment has the highest profit in 2018 เป็นคำถามที่ต้องการทราบว่า Segment ได้ผลรวมกำไรสูงสุด และสูงสุดเท่าไหร่
เริ่มจากการกรองออกมาเฉพาะปี 2018 จากนั้นเข้า groupby(), agg() และสำคัญที่ sort_value() เพื่อจัดเรียงผลรวมกำไรจากมากไปน้อย ก่อนจะแสดงผลออกมา
# which Segment has the highest profit in 2018
df_2018 = df[df['Order Date'].dt.year == 2018]
df_2018['year'] = df['Order Date'].dt.year
hg_profit = df_2018.groupby(['year', 'Segment'])['Profit'].agg(['sum']).reset_index()\
.sort_values('sum', ascending=False)
print(f"The highest profit in 2018 is the {hg_profit.iloc[0, 1]} segment. That make a profit of ${hg_profit.iloc[0, 2]}")
hg_profit

คำถาม 11 – which top 5 States have the least total sales between 15 April 2019 – 31 December 2019 เป็นคำถามที่ต้องการข้อมูลตาม Range วันที่ จึงใช้ pd.to_datetime() เพื่อสร้างวันที่เริ่มและสิ้นสุด ก่อนที่จะนำไป filter range_df ออกมา จากนั้นค่อยใช้ groupby(), agg() และ sort_value() เพื่อจัดเรียงผลรวมยอดขายจากน้อยไปมาก และใช้ head(5) เพื่อตัดมาเฉพาะ 5 อันดับแรก
# which top 5 States have the least total sales between 15 April 2019 - 31 December 2019
date_str = pd.to_datetime('2019-04-15')
date_end = pd.to_datetime('2019-12-31')
range_df = df[ (df['Order Date'] >= date_str) & (df['Order Date'] <= date_end) ][['State', 'Sales']]
top5_least = range_df.groupby(['State'])['Sales'].agg(['sum']).reset_index()\
.sort_values('sum', ascending=True).head(5)
top5_least

คำถาม 12 – what is the proportion of total sales (%) in West + Central in 2019 e.g. 25% คำถามสั้นๆ แต่โค้ดอย่างยาว ฮ่าๆๆ เริ่มจาก filter rows และ columns เฉพาะที่ต้องใช้ออกมาก่อน
จากนั้นใช้ groupby(), agg() และ reset_index() เพื่อให้ได้ยอดขายรวม total_sales ของแต่ละ Region ออกมาก่อน
เข้าสู่การคำนวนสัดส่วน ซึ่งต้องหาผลรวมการขายทั้งหมดก่อน (ทุกภูมิภาค) ไว้ในตัวแปร all_sales จากนั้นคำนวน percentage ออกมา
# what is the proportion of total sales (%) in West + Central in 2019 e.g. 25%
df_2019 = df[ (df['Order Date'].dt.year == 2019) ][['Order Date', 'Region', 'Sales']]
df_2019['year'] = df['Order Date'].dt.year
df_2019 = df_2019.groupby(['Region']).agg(total_sales=('Sales', 'sum')).reset_index()
# คำนวณร้อยละ
all_sales = df_2019['total_sales'].sum()
df_2019['percentage'] = (df_2019['total_sales'] / all_sales) * 100
# slice value for West & Central
west_prop = df_2019.query("Region == 'West'")['percentage'].values[0]
cent_prop = df_2019.query("Region == 'Central'")['percentage'].values[0]
print(f"The proportion of total sales in West & Central in 2019 is {round(west_prop+cent_prop, 2)} %")
df_2019
แต่คำถามต้องการผลรวมสัดส่วนของ West + Central จึงต้องจับมาบวกกันอีกครั้งในขั้นตอนสุดท้าย

คำถาม 13 – find top 10 popular products in terms of number of orders vs. total sales during 2019-2020 คำ(ถาม)ง่ายๆ แต่ความหมายสุดลึกล้ำอีกแล้ว
ต้องการเทียบระหว่างสินค้าที่มียอดสั่งซื้อมากที่สุด 10 อันดับ เทียบกับสินค้าที่ยอดขายสูงที่สุด 10 อันดับ บร๊ะเจ้า 🫥 ก่อนอื่นกรองเฉพาะคอลัมน์และแถวที่ต้องใช้ออกมาก่อน
# find top 10 popular products in terms of number of orders vs. total sales during 2019-2020
df_prod = df[ (df['Order Date'].dt.year >= 2019) | (df['Order Date'].dt.year <= 2020) ][['Order Date', 'Product Name', 'Order ID', 'Sales']]
คำนวณฝั่งยอดสั่งซื้อก่อน ใช้ฟังก์ชัน groupby(), agg() เพื่อนับจำนวน และตัดมาเฉพาะ head(10) จากนั้นเพิ่มคอลัมน์ pd.RangeIndex(10) เพื่อเป็น key index ที่เราจะใช้ join df เข้าด้วยกัน (ประมาณว่าอันดับที่ 1 ชนกับ อันดับที่ 1 อ่าครับ)
# terms of number of orders
n_order = df_prod.groupby(['Product Name']).agg(n_orders=('Order ID', 'count'))\
.reset_index().sort_values('n_orders', ascending=False).head(10)
n_order['top_n'] = pd.RangeIndex(10)
ถัดมาคำนวณฝั่งยอดขายสูงที่สุด ซึ่งใช้สเต็ปเดิมเลยแต่คำนวณจาก ผลรวม(sum)ยอดขาย แทน
# terms of total sales
t_sale = df_prod.groupby(['Product Name']).agg(t_sales=('Sales', 'sum'))\
.reset_index().sort_values('t_sales', ascending=False).head(10)
t_sale['top_n'] = pd.RangeIndex(10)
top_10 = pd.merge(n_order, t_sale, on='top_n')
top_10 = top_10[['top_n', 'Product Name_x', 'n_orders', 'Product Name_y', 't_sales']] # re-columns
top_10 = top_10.rename(columns={"Product Name_x": "Product_byOrders", "Product Name_y": "Product_bySales"}) # rename-columns
top_10
สุดท้ายนำทั้ง 2 df มา merge() กันโดยใช้ key index (top_n) ที่เราได้เพิ่มคอลัมน์ไว้ จากนั้น drop คอลัมน์ที่ไม่จำเป็น และเปลี่ยนชื่อคอลัมน์ให้เข้าใจได้ และแสดงผลออกมา
จากมูลที่ได้ในฝั่งยอดสั่งซื้อจะเป็นพวกอุปกรณ์สำนักงานที่มียอดสั่งซื้อมากที่สุด และในฝั่งของยอดขายสูงที่สุดจะเป็นพวกเครื่องใช้ไฟฟ้าที่ใช้ในสำนักงานนั่นเอง

คำถาม 14 – plot at least 2 plots, any plot you think interesting โอเคตั้งคำถามเอง เริ่มที่ Total sales by Segment in 2020
จึงทำดึงเฉพาะคอลัมน์และแถวที่ต้องใช้ออกมาไว้ใน df_plot จากนั้นใช้ groupby(), agg() เพื่อคำนวณยอดขาย จากนั้นส่งออกไป plot() เป็น bar chart
# plot at least 2 plots, any plot you think interesting :)
df_plot = df[ df['Order Date'].dt.year == 2020 ][['Order Date', 'Segment', 'Sales', 'Profit']]
# graph 1: Total sales by Segment in 2020
segment_sales = df_plot.groupby(['Segment']).agg(t_sales=('Sales', 'sum')).reset_index()
segment_sales[['Segment', 't_sales']].plot(x='Segment', y='t_sales' ,kind='barh', color=['salmon', 'orange', 'gold']);

# graph 2: Total Profit Trend in 2020
df_plot['month_'] = df_plot['Order Date'].dt.month
date_profit = df_plot.groupby(['month_']).agg(t_profit=('Profit', 'sum')).reset_index()
date_profit[['month_', 't_profit']].plot(x='month_', y='t_profit' ,kind='line', color='darkgreen');
สำหรับ Total Profit Trend in 2020 ใช้ไว้ที่การที่ไม่ต่างกันมากนั้น แต่ต้องทำการสร้างคอลัมน์ month_ ออกมาก่อนโดยใช้ .dt.month เพื่อพล็อตเป็น line chart ตามลำดับเวลานั้นเอง

คำถาม 15 – use np.where() to create new column in dataframe to help you answer your own questions ก็คือเราต้องการแบ่ง segment ลูกค้าจากยอดขายที่สั่งซื้อเข้ามาโดยมี assumption ว่า..
| segment_sales | criteria (sales) |
| high | > 5000 |
| middle | < 5000 & >= 100 |
| low | < 100 |
# use np.where() to create new column in dataframe to help you answer your own questions
import numpy as np
# segment_sales = { high : > 5000, middle : < 5000 & >= 100, low : < 100 }
df['segment_sales'] = np.where(df['Sales'] > 5000, 'high', np.where(df['Sales'] < 100, 'low', 'middle'))
# Number of product orders grouped by sales segments at high, middle and low levels
seg_sale = df[ ['Sales', 'segment_sales'] ]
seg_sale.groupby(['segment_sales']).agg(n_value=('segment_sales', 'count')).reset_index()
ซึ่งต้องใช้ np.where() จาก numpy มาช่วย จากนั้นก็นับจำนวนตาม order ที่สั่งซื้อเข้ามาได้ผลลัพธ์ดังนี้

ฟิ้วว 🙂↕️ ยาวมากกก เป็นไงกันมั้งครับ 15 ข้อคำถามจาก 1 dataset
ถ้าชอบกด 💗 หัวใจ เป็นกำลังใจให้หนุ่มด้วยนะครับ ขอบคุณค้าบบบ 👏






