Example - Don't Repeat Yourself (DRY)
Table of Contents
1) ตัวอย่างโค้ดที่พบได้บ่อยในสถานการณ์จริง
ข้อมูลจาก https://data.go.th/dataset/ulg-station
ดาวน์โหลดมาลองได้ด้วยคำสั่ง
!wget https://data.go.th/dataset/d8a056fe-3ded-4620-944e-10f87c4f143a/resource/9c33fabf-d564-4bde-bc7c-14195c5c578d/download/02-ulg-21-01jan-sta.csv
!wget https://data.go.th/dataset/d8a056fe-3ded-4620-944e-10f87c4f143a/resource/768ed19a-31af-4c6f-918b-366daed07e41/download/02-ulg-21-11nov-sta.csv
import pandas as pd
พย = pd.read_csv("02-ulg-21-11nov-sta.csv", encoding="ISO-8859-11")
พย.columns = พย.loc[3]
พย = พย.loc[5:94]
พย = พย.dropna(subset=["ลำดับที่"])
พย = พย.reset_index()
del พย["index"]
พย.columns.name = None
พย = พย.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
พย['ปตท.'] = พย['ปตท.'].str.replace(",","").astype(float)
พย['เชลล์'] = พย['เชลล์'].str.replace(",","").astype(float)
พย['เชฟรอน(ไทย)'] = พย['เชฟรอน(ไทย)'].str.replace(",","").astype(float)
พย['ซัสโก้'] = พย['ซัสโก้'].str.replace(",","").astype(float)
พย['พีทีจี'] = พย['พีทีจี'].str.replace(",","").astype(float)
พย['ไทยออยล์'] = พย['ไทยออยล์'].str.replace(",","").astype(float)
พย['ซัสโก้ ดีลเลอร์ส'] = พย['ซัสโก้ ดีลเลอร์ส'].str.replace(",","").astype(float)
พย['สยามเฆมี'] = พย['สยามเฆมี'].str.replace(",","").astype(float)
พย['ปตท. บริหารธุรกิจค้าปลีก'] = พย['ปตท. บริหารธุรกิจค้าปลีก'].str.replace(",","").astype(float)
พย['ปตท. น้ำมันและการค้าปลีก'] = พย['ปตท. น้ำมันและการค้าปลีก'].str.replace(",","").astype(float)
พย["รวมทั้งสิ้น(พันลิตร)"] = พย["รวมทั้งสิ้น(พันลิตร)"].str.replace(",","").astype("float")
พย.head()
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 3.0 | NaN | 709.0 | 560.0 | 13.0 | 150.0 | 145.0 | 34.0 | 131.0 | 1173.0 | 2919.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 69.0 | 36.0 | NaN | 21.0 | 29.0 | NaN | NaN | 138.0 | 294.0 |
2 | 3 | นนทบุรี | NaN | NaN | 147.0 | 36.0 | NaN | 67.0 | 66.0 | NaN | 18.0 | 246.0 | 580.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 51.0 | 39.0 | NaN | 39.0 | NaN | NaN | 6.0 | 111.0 | 247.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 27.0 | NaN | NaN | 12.0 | NaN | NaN | 42.0 | 93.0 | 174.0 |
มค = pd.read_csv("02-ulg-21-01jan-sta.csv", encoding="ISO-8859-11")
มค.columns = มค.loc[3]
มค = มค.loc[5:94]
มค = มค.dropna(subset=["ลำดับที่"])
มค = มค.reset_index()
del มค["index"]
มค.columns.name = None
มค = มค.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
มค['ปตท.'] = มค['ปตท.'].str.replace(",","").astype(float)
มค['เชลล์'] = มค['เชลล์'].str.replace(",","").astype(float)
มค['เชฟรอน(ไทย)'] = มค['เชฟรอน(ไทย)'].str.replace(",","").astype(float)
มค['ซัสโก้'] = มค['ซัสโก้'].str.replace(",","").astype(float)
มค['พีทีจี'] = มค['พีทีจี'].str.replace(",","").astype(float)
มค['ไทยออยล์'] = มค['ไทยออยล์'].str.replace(",","").astype(float)
มค['ซัสโก้ ดีลเลอร์ส'] = มค['ซัสโก้ ดีลเลอร์ส'].str.replace(",","").astype(float)
มค['สยามเฆมี'] = มค['สยามเฆมี'].str.replace(",","").astype(float)
มค['ปตท. บริหารธุรกิจค้าปลีก'] = มค['ปตท. บริหารธุรกิจค้าปลีก'].str.replace(",","").astype(float)
มค['ปตท. น้ำมันและการค้าปลีก'] = มค['ปตท. น้ำมันและการค้าปลีก'].str.replace(",","").astype(float)
มค["รวมทั้งสิ้น(พันลิตร)"] = มค["รวมทั้งสิ้น(พันลิตร)"].str.replace(",","").astype("float")
มค.head()
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 28.0 | NaN | 845.0 | 610.0 | 9.0 | 169.0 | 179.0 | 23.0 | 136.0 | 1300.0 | 3299.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 83.0 | 52.0 | NaN | 26.0 | 36.0 | NaN | NaN | 140.0 | 337.0 |
2 | 3 | นนทบุรี | NaN | NaN | 209.0 | 42.0 | NaN | 84.0 | 79.0 | NaN | 29.0 | 258.0 | 701.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 66.0 | 53.0 | NaN | 54.0 | NaN | NaN | 6.0 | 99.0 | 279.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 40.0 | NaN | NaN | 6.0 | NaN | NaN | 25.0 | 118.0 | 189.0 |
2) Don't Repeat Yourself (DRY)
ในการพัฒนาซอฟท์แวร์ หรือการเขียนโปรแกรมจะมีหลักการ Don't Repeat Yourself (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) ซึ่งใจความหลักคือทุกครั้งที่เราทำซ้ำ (copy-paste โค้ดตัวเอง) เราสามารถพัฒนาวิธีการเขียนโปรแกรมนั้นได้
2.1) การใช้ loop ในการ DRY
จากโค้ดเดิม
พย.columns = พย.loc[3]
พย = พย.loc[5:94]
พย = พย.dropna(subset=["ลำดับที่"])
พย = พย.reset_index()
del พย["index"]
พย.columns.name = None
พย = พย.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
พย['ปตท.'] = พย['ปตท.'].str.replace(",","").astype(float)
พย['เชลล์'] = พย['เชลล์'].str.replace(",","").astype(float)
พย['เชฟรอน(ไทย)'] = พย['เชฟรอน(ไทย)'].str.replace(",","").astype(float)
พย['ซัสโก้'] = พย['ซัสโก้'].str.replace(",","").astype(float)
พย['พีทีจี'] = พย['พีทีจี'].str.replace(",","").astype(float)
พย['ไทยออยล์'] = พย['ไทยออยล์'].str.replace(",","").astype(float)
พย['ซัสโก้ ดีลเลอร์ส'] = พย['ซัสโก้ ดีลเลอร์ส'].str.replace(",","").astype(float)
พย['สยามเฆมี'] = พย['สยามเฆมี'].str.replace(",","").astype(float)
พย['ปตท. บริหารธุรกิจค้าปลีก'] = พย['ปตท. บริหารธุรกิจค้าปลีก'].str.replace(",","").astype(float)
พย['ปตท. น้ำมันและการค้าปลีก'] = พย['ปตท. น้ำมันและการค้าปลีก'].str.replace(",","").astype(float)
พย["รวมทั้งสิ้น(พันลิตร)"] = พย["รวมทั้งสิ้น(พันลิตร)"].str.replace(",","").astype("float")
พย.head()
จะเห็นว่าส่วนที่ซ้ำกันมากที่สุดคือ
พย['ปตท.'] = พย['ปตท.'].str.replace(",","").astype(float)
พย['เชลล์'] = พย['เชลล์'].str.replace(",","").astype(float)
พย['เชฟรอน(ไทย)'] = พย['เชฟรอน(ไทย)'].str.replace(",","").astype(float)
พย['ซัสโก้'] = พย['ซัสโก้'].str.replace(",","").astype(float)
พย['พีทีจี'] = พย['พีทีจี'].str.replace(",","").astype(float)
พย['ไทยออยล์'] = พย['ไทยออยล์'].str.replace(",","").astype(float)
พย['ซัสโก้ ดีลเลอร์ส'] = พย['ซัสโก้ ดีลเลอร์ส'].str.replace(",","").astype(float)
พย['สยามเฆมี'] = พย['สยามเฆมี'].str.replace(",","").astype(float)
พย['ปตท. บริหารธุรกิจค้าปลีก'] = พย['ปตท. บริหารธุรกิจค้าปลีก'].str.replace(",","").astype(float)
พย['ปตท. น้ำมันและการค้าปลีก'] = พย['ปตท. น้ำมันและการค้าปลีก'].str.replace(",","").astype(float)
พย["รวมทั้งสิ้น(พันลิตร)"] = พย["รวมทั้งสิ้น(พันลิตร)"].str.replace(",","").astype("float")
ซึ่งในการเขียนโปรแกรมเรามีโครงสร้างสำหรับการทำซ้ำอยู่แล้ว (loop) จึงสามารถนำมาใช้ได้ เช่น
df = pd.read_csv("02-ulg-21-11nov-sta.csv", encoding="ISO-8859-11")
df.columns = df.loc[3]
df = df.loc[5:94]
df = df.dropna(subset=["ลำดับที่"])
df = df.reset_index()
del df["index"]
df.columns.name = None
df = df.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
for col in ['ปตท.', 'เชลล์', 'เชฟรอน(ไทย)', 'ซัสโก้',
'พีทีจี', 'ไทยออยล์', 'ซัสโก้ ดีลเลอร์ส', 'สยามเฆมี',
'ปตท. บริหารธุรกิจค้าปลีก', 'ปตท. น้ำมันและการค้าปลีก',
'รวมทั้งสิ้น(พันลิตร)']:
df[col] = df[col].str.replace(",", "").astype(float)
df
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 3.0 | NaN | 709.0 | 560.0 | 13.0 | 150.0 | 145.0 | 34.0 | 131.0 | 1173.0 | 2919.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 69.0 | 36.0 | NaN | 21.0 | 29.0 | NaN | NaN | 138.0 | 294.0 |
2 | 3 | นนทบุรี | NaN | NaN | 147.0 | 36.0 | NaN | 67.0 | 66.0 | NaN | 18.0 | 246.0 | 580.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 51.0 | 39.0 | NaN | 39.0 | NaN | NaN | 6.0 | 111.0 | 247.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 27.0 | NaN | NaN | 12.0 | NaN | NaN | 42.0 | 93.0 | 174.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
72 | 73 | ตรัง | NaN | NaN | 20.0 | 75.0 | NaN | NaN | NaN | NaN | NaN | 28.0 | 123.0 |
73 | 74 | พัทลุง | NaN | NaN | 9.0 | NaN | 6.0 | NaN | NaN | NaN | NaN | 3.0 | 18.0 |
74 | 75 | ปัตตานี | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | 4.0 |
75 | 76 | ยะลา | NaN | NaN | NaN | NaN | 37.0 | NaN | NaN | NaN | NaN | 42.0 | 79.0 |
76 | 77 | นราธิวาส | NaN | NaN | 6.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 6.0 |
77 rows × 13 columns
ทั้งนี้ ค่า list ['ปตท.', 'เชลล์', 'เชฟรอน(ไทย)', 'ซัสโก้', 'พีทีจี', 'ไทยออยล์', 'ซัสโก้ ดีลเลอร์ส', 'สยามเฆมี', 'ปตท. บริหารธุรกิจค้าปลีก', 'ปตท. น้ำมันและการค้าปลีก', 'รวมทั้งสิ้น(พันลิตร)']
ที่เรานำมาวนลูปนั้นเป็นชื่อคอลัมน์ เพราะฉะนั้นเราสามารถดึงค่าพวกนี้มาจาก df.columns
ได้เช่นกัน
df.columns
Index(['ลำดับที่', 'จังหวัด', 'ปตท.', 'เชลล์', 'เชฟรอน(ไทย)', 'ซัสโก้',
'พีทีจี', 'ไทยออยล์', 'ซัสโก้ ดีลเลอร์ส', 'สยามเฆมี',
'ปตท. บริหารธุรกิจค้าปลีก', 'ปตท. น้ำมันและการค้าปลีก',
'รวมทั้งสิ้น(พันลิตร)'],
dtype='object')
df.columns[3:]
Index(['เชลล์', 'เชฟรอน(ไทย)', 'ซัสโก้', 'พีทีจี', 'ไทยออยล์',
'ซัสโก้ ดีลเลอร์ส', 'สยามเฆมี', 'ปตท. บริหารธุรกิจค้าปลีก',
'ปตท. น้ำมันและการค้าปลีก', 'รวมทั้งสิ้น(พันลิตร)'],
dtype='object')
เก็บค่าที่ต้องการไว้ในตัวแปร
columns_to_convert = df.columns[3:]
นำมารวมกับของเดิม จะได้เป็น
df = pd.read_csv("02-ulg-21-11nov-sta.csv", encoding="ISO-8859-11")
df.columns = df.loc[3]
df = df.loc[5:94]
df = df.dropna(subset=["ลำดับที่"])
df = df.reset_index()
del df["index"]
df.columns.name = None
df = df.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
columns_to_convert = df.columns[3:]
for col in columns_to_convert:
df[col] = df[col].str.replace(",", "").astype(float)
df
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 3 | NaN | 709.0 | 560.0 | 13.0 | 150.0 | 145.0 | 34.0 | 131.0 | 1173.0 | 2919.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 69.0 | 36.0 | NaN | 21.0 | 29.0 | NaN | NaN | 138.0 | 294.0 |
2 | 3 | นนทบุรี | NaN | NaN | 147.0 | 36.0 | NaN | 67.0 | 66.0 | NaN | 18.0 | 246.0 | 580.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 51.0 | 39.0 | NaN | 39.0 | NaN | NaN | 6.0 | 111.0 | 247.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 27.0 | NaN | NaN | 12.0 | NaN | NaN | 42.0 | 93.0 | 174.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
72 | 73 | ตรัง | NaN | NaN | 20.0 | 75.0 | NaN | NaN | NaN | NaN | NaN | 28.0 | 123.0 |
73 | 74 | พัทลุง | NaN | NaN | 9.0 | NaN | 6.0 | NaN | NaN | NaN | NaN | 3.0 | 18.0 |
74 | 75 | ปัตตานี | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | 4.0 |
75 | 76 | ยะลา | NaN | NaN | NaN | NaN | 37.0 | NaN | NaN | NaN | NaN | 42.0 | 79.0 |
76 | 77 | นราธิวาส | NaN | NaN | 6.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 6.0 |
77 rows × 13 columns
จะ clean ข้อมูลของหนึ่งเดือนได้กระชับมากขึ้น
2.2) การใช้ Function ช่วยในการ DRY
และจากเดิมที่ต้องทำซ้ำกับหลาย ๆ เดือน ในส่วนนี้เราสามารถนำ concept function มาใช้ได้
โดยสังเกตว่าในแต่ละเดือนเราใช้คำสั่งเหมือนกันทั้งหมด แตกต่างกันเพียงแค่ชื่อไฟล์เท่านั้น เพราะฉะนั้นสามารถทำเป็น function ที่รับชื่อไฟล์ (filename
) เป็น parameter ได้ดังนี้
def clean_one_month(filename):
df = pd.read_csv(filename, encoding="ISO-8859-11")
df.columns = df.loc[3]
df = df.loc[5:94]
df = df.dropna(subset=["ลำดับที่"])
df = df.reset_index()
del df["index"]
df.columns.name = None
df = df.rename(columns={"รวมทั้งสิ้น" : "รวมทั้งสิ้น(พันลิตร)"})
columns_to_convert = df.columns[3:]
for col in columns_to_convert:
df[col] = df[col].str.replace(",", "").astype(float)
return df
df = clean_one_month("02-ulg-21-11nov-sta.csv")
df
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 3 | NaN | 709.0 | 560.0 | 13.0 | 150.0 | 145.0 | 34.0 | 131.0 | 1173.0 | 2919.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 69.0 | 36.0 | NaN | 21.0 | 29.0 | NaN | NaN | 138.0 | 294.0 |
2 | 3 | นนทบุรี | NaN | NaN | 147.0 | 36.0 | NaN | 67.0 | 66.0 | NaN | 18.0 | 246.0 | 580.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 51.0 | 39.0 | NaN | 39.0 | NaN | NaN | 6.0 | 111.0 | 247.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 27.0 | NaN | NaN | 12.0 | NaN | NaN | 42.0 | 93.0 | 174.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
72 | 73 | ตรัง | NaN | NaN | 20.0 | 75.0 | NaN | NaN | NaN | NaN | NaN | 28.0 | 123.0 |
73 | 74 | พัทลุง | NaN | NaN | 9.0 | NaN | 6.0 | NaN | NaN | NaN | NaN | 3.0 | 18.0 |
74 | 75 | ปัตตานี | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | 4.0 |
75 | 76 | ยะลา | NaN | NaN | NaN | NaN | 37.0 | NaN | NaN | NaN | NaN | 42.0 | 79.0 |
76 | 77 | นราธิวาส | NaN | NaN | 6.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 6.0 |
77 rows × 13 columns
df = clean_one_month("02-ulg-21-01jan-sta.csv")
df
ลำดับที่ | จังหวัด | ปตท. | เชลล์ | เชฟรอน(ไทย) | ซัสโก้ | พีทีจี | ไทยออยล์ | ซัสโก้ ดีลเลอร์ส | สยามเฆมี | ปตท. บริหารธุรกิจค้าปลีก | ปตท. น้ำมันและการค้าปลีก | รวมทั้งสิ้น(พันลิตร) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | กรุงเทพมหานคร | 28 | NaN | 845.0 | 610.0 | 9.0 | 169.0 | 179.0 | 23.0 | 136.0 | 1300.0 | 3299.0 |
1 | 2 | สมุทรปราการ | NaN | NaN | 83.0 | 52.0 | NaN | 26.0 | 36.0 | NaN | NaN | 140.0 | 337.0 |
2 | 3 | นนทบุรี | NaN | NaN | 209.0 | 42.0 | NaN | 84.0 | 79.0 | NaN | 29.0 | 258.0 | 701.0 |
3 | 4 | ปทุมธานี | NaN | NaN | 66.0 | 53.0 | NaN | 54.0 | NaN | NaN | 6.0 | 99.0 | 279.0 |
4 | 5 | พระนครศรีอยุธยา | NaN | NaN | 40.0 | NaN | NaN | 6.0 | NaN | NaN | 25.0 | 118.0 | 189.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
72 | 73 | ตรัง | NaN | NaN | 25.0 | 74.0 | NaN | NaN | NaN | NaN | NaN | 61.0 | 160.0 |
73 | 74 | พัทลุง | NaN | NaN | 9.0 | NaN | 3.0 | NaN | NaN | NaN | NaN | 6.0 | 18.0 |
74 | 75 | ปัตตานี | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4.0 | 4.0 |
75 | 76 | ยะลา | NaN | NaN | NaN | NaN | 15.0 | NaN | NaN | NaN | NaN | 50.0 | 65.0 |
76 | 77 | นราธิวาส | NaN | NaN | 12.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 12.0 |
77 rows × 13 columns
3) สรุป
ทั้งนี้การ DRY ก็ใช้เวลาในการเขียนมากขึ้นเช่นกัน จึงนำไปปรับใช้ได้ตามความถนัดและความเหมาะสมในสถานการณ์ต่าง ๆ กัน