Data Warehouse

ดิกชันนารี (Dictionary)

ดิกชันนารีเป็นวัตถุที่เก็บค่าข้อมูลหลายๆตัวเช่นเดียวกับลิสต์และทูเพิล แต่จะต่างกันตรงที่ต้องประกอบไปด้วย ๒ ส่วน คือ คีย์ (key) กับ ค่า (value) อยู่ด้วยกันเหมือนเป็นคู่อันดับ ลิสต์และทูเพิลจะใช้เลขดัชนีเป็นตัวชี้ถึงค่าภายในลำดับ แต่ว่าในดิกชันนารีจะใช้คีย์แทน คีย์นั้นเหมือนเป็นสิ่งที่มาใช้แทนเลขดัชนีในลิสต์หรือทูเพิล คือแทนที่จะใช้ตัวเลขเพื่อชี้สมาชิกก็เปลี่ยนมาใช้คีย์ซึ่งเป็นอะไรก็ได้ที่กำหนดขึ้นเอง

การสร้างและเข้าถึงข้อมูลในดิกชันนารี

ดิกชันนารีสามารถสร้างได้โดยใช้วงเล็บปีกกาเช่นเดียวกับเซ็ต แต่ภายในใส่สมาชิกเป็นคู่ๆโดยแต่ละคู่ใส่คีย์ทางซ้ายแล้วคั่นด้วยโคลอน : แล้วตามด้วยค่า จากนั้นก็คั่นด้วยจุลภาค , แล้วตามด้วยคู่ถัดไป

ตัวอย่าง ดิกชันนารีที่เก็บข้อมูลตึกสูงในไทย

tueksung = {'มหานคร': 313,  'ใบหยก 2': 304,  'อมันตา ลุมพินี': 212,  
'อื้อจือเหลียง': 160,  'จัตุรัสจามจุรี': 157,  'ใบหยก 1': 151}

เวลาต้องการเข้าถึงข้อมูลก็ใช้ชื่อดิกชันนารีตามด้วย [ ] ซึ่งใส่ชื่อคีย์

print(tueksung['จัตุรัสจามจุรี'])  # ได้ 157  
print(tueksung['ใบหยก 2'])  # ได้ 304

ดิกชันนารีไม่สามารถใช้ตัวเลขเพื่อระบุสมาชิกโดยอาศัยลำดับได้อย่างในลิสต์ ต้องใช้คีย์ตามที่ใส่เข้าไปเท่านั้น

print(tueksung[1])  # ได้ KeyError: 1

ทั้งคีย์และค่าจะเป็นข้อมูลชนิดไหนก็ได้ อาจเป็นตัวเลขก็ได้ เช่น ดิกชันนารีที่เก็บชื่อของโปเกมอน

pokemon = {151:  'mew',  
152:  'chicorita', 153:  'bayleaf',154:  'meganium',  
155:  'hinoarashi', 156:  'magmarashi',157:  'bakphoon',  
158:  'waninoko',159:  'alligates', 160:  'ordile'}  
print(pokemon[151])  # ได้ mew

กรณีที่ใช้คีย์เป็นตัวเลขจำนวนเต็มแบบนี้ดูเผินๆอาจดูคล้ายกับลิสต์หรือทูเพิล แต่ต่างกันตรงที่ตัวเลขที่ใส่ต้องเป็นเลขที่มีอยู่ในคีย์เท่านั้นและไม่จำเป็นต้องไล่จาก 0

การซ้ำกันของคีย์และค่า

ภายในดิกชันนารีหนึ่งๆ ค่าอาจจะซ้ำกันได้ แต่คีย์จะซ้ำกันไม่ได้

khanaen = {'ฟิสิกส์':97,'ฟิสิกส์':91,'เคมี':96,'คณิตศาสตร์':87}  
print(khanaen)  # ได้ {'ฟิสิกส์': 91, 'เคมี': 96, 'คณิตศาสตร์': 87}

จะเห็นว่าใส่ค่าซ้ำลงไปแต่สุดท้ายก็เหลือแค่ตัวเดียว ทั้งนี้เพราะว่าคีย์เป็นสิ่งที่ต้องใช้อ้างอิงเพื่อชี้ถึงค่าในดิกชันนารี จึงไม่สามารถซ้ำกันได้ เช่นเดียวกับฟังก์ชันทางคณิตศาสตร์ที่โดเมนต้องไม่ซ้ำ ถ้าซ้ำกันละก็พอจะเรียกหาค่าก็จะเกิดความคลุมเครือไม่รู้จะเรียกตัวไหน ในขณะที่ค่านั้นต่อให้ใส่ซ้ำกันจะไม่มีปัญหาอะไร

khanaen = {'ฟิสิกส์ 1':97,'ฟิสิกส์ 2':91,'เคมี 1':91,'เคมี 2':97}  
print(khanaen)  # ได้ {'ฟิสิกส์ 1': 97, 'ฟิสิกส์ 2': 91, 'เคมี 1': 91, 'เคมี 2': 97}

ในที่นี้ค่าของ “ฟิสิกส์ 1” ซ้ำกับ “เคมี 2” และ “เคมี 1” ซ้ำกับ “ฟิสิกส์ 2” จะเห็นว่าสามารถซ้ำกันได้

keys, values และ items

รายการคีย์ทั้งหมดสามารถแสดงได้โดยเมธอด keys ตัวอย่าง ดิกชันนารีแสดงรายชื่อเมืองเอก (เรียกว่าเสิ่งฮุ่ย (shěnghuì, 省会) ในภาษาจีน) ของมณฑลในจีน

shenghui = {'กวางตุ้ง':  'กว่างโจว','เจ้อเจียง':  'หางโจว',  
'เหลียวหนิง':  'เสิ่นหยาง','เหอเป่ย์':  'สือเจียจวาง',  
'เหอหนาน':  'เจิ้งโจว','ฝูเจี้ยน':  'ฝูโจว',  
'ทิเบต':  'ลาซ่า','ซินเจียง':  'อูหลู่มู่ฉี'}  
print(shenghui.keys())

ผลที่ได้

dict_keys(['กวางตุ้ง', 'เจ้อเจียง', 'เหลียวหนิง', 'เหอเป่ย์', 'เหอหนาน', 'ฝูเจี้ยน', 'ทิเบต', 'ซินเจียง'])

ข้อมูลที่ได้นี้เป็นชนิด dict_keys ซึ่งถือเป็นคลาสเฉพาะคลาสนึง ไม่สามารถเปลี่ยนแปลงแก้ไขค่าในนี้ได้เช่นเดียวกับทูเพิล ไม่สามารถใช้ตัวเลขชี้ถึงค่าข้างในได้ แต่สามารถใช้ in เพื่อตรวจดูสมาชิกข้างในได้ สามารถแปลงเป็นลิสต์หรือทูเพิล หรือใช้ join เพื่อเชื่อมกันได้

'ไต้หวัน'  in  shenghui.keys()  # ได้ False  
'ทิเบต'  in  shenghui.keys()  # ได้ True  
{'เหอหนาน','เหอเป่ย์'}.issubset(shenghui.keys())  # ได้ True  
{'เหอหนาน','เหอเป่ย์','หูหนาน','หูเป่ย์'}.intersection( shenghui.keys())  # ได้ {'เหอหนาน', 'เหอเป่ย์'}  
print(tuple(shenghui.keys()))  # ได้ ('กวางตุ้ง', 'เจ้อเจียง', 'เหลียวหนิง', 'เหอเป่ย์', 'เหอหนาน', 'ฝูเจี้ยน', 'ทิเบต', 'ซินเจียง')  
print('|'.join(shenghui.keys()))  # ได้ กวางตุ้ง|เจ้อเจียง|เหลียวหนิง|เหอเป่ย์|เหอหนาน|ฝูเจี้ยน|ทิเบต|ซินเจียง

ส่วนค่าในดิกชันนารีสามารถแสดงทั้งหมดโดยเมธอด values

print(shenghui.values())

จะได้ออกมาเป็นข้อมูลชนิด dict_values ซึ่งก็มีคุณสมบัติเหมือนกับ dict_keys

dict_values(['กว่างโจว', 'หางโจว', 'เสิ่นหยาง', 'สือเจียจวาง', 'เจิ้งโจว', 'ฝูโจว', 'ลาซ่า', 'อูหลู่มู่ฉี'])

และถ้าต้องการให้แสดงคีย์กับค่าคู่กันในรูปของทูเพิลให้ใช้เมธอด items

print(shenghui.items())

จะได้ข้อมูลชนิด dict_items

dict_items([('กวางตุ้ง', 'กว่างโจว'), ('เจ้อเจียง', 'หางโจว'), ('เหลียวหนิง', 'เสิ่นหยาง'), ('เหอเป่ย์', 'สือเจียจวาง'), ('เหอหนาน', 'เจิ้งโจว'), ('ฝูเจี้ยน', 'ฝูโจว'), ('ทิเบต', 'ลาซ่า'), ('ซินเจียง', 'อูหลู่มู่ฉี')])

***ในไพธอน 2 เมธอด keys, values และ items จะได้ผลเป็นลิสต์ แทนที่จะได้เป็นคลาสเฉพาะ รายละเอียด

การใช้ for คู่กับดิกชันนารี

เมื่อใช้ for กับดิกชันนารี ตัวแปรจะไล่ตามคีย์ ไม่ใช่ค่า

for  s  in  shenghui:  
print(s,end=' ')  # ได้ กวางตุ้ง เจ้อเจียง เหลียวหนิง เหอเป่ย์ เหอหนาน ฝูเจี้ยน ทิเบต ซินเจียง

แต่ก็สามารถเข้าถึงค่าได้ทันทีโดยใส่คีย์ใน [ ]

for  s  in  shenghui:  
print(shenghui[s],end=' ')  # ได้ กว่างโจว หางโจว เสิ่นหยาง สือเจียจวาง เจิ้งโจว ฝูโจว ลาซ่า อูหลู่มู่ฉี

หรือใช้เมธอด values ก็สามารถไล่ตามค่าได้

for  v  in  shenghui.values():  
print(v,end=' ')  # ได้ กว่างโจว หางโจว เสิ่นหยาง สือเจียจวาง เจิ้งโจว ฝูโจว ลาซ่า อูหลู่มู่ฉี  

หากจะใช้ทั้งคีย์และค่าก็อาจใช้เมธอด items

for  v  in  shenghui.items():  
print(v[0],v[1],sep='>',end=' ')  # กวางตุ้ง>กว่างโจว เจ้อเจียง>หางโจว เหลียวหนิง>เสิ่นหยาง เหอเป่ย์>สือเจียจวาง เหอหนาน>เจิ้งโจว ฝูเจี้ยน>ฝูโจว ทิเบต>ลาซ่า ซินเจียง>อูหลู่มู่ฉี

การเพิ่มข้อมูลและแก้ไขดิกชันนารี

การเพิ่มเติมข้อมูลสามารถทำได้โดยใช้ = เพื่อป้อนค่าใหม่ลงไปได้เลย ตัวอย่าง พื้นที่ของจังหวัดในไทย

phuenthi = {'นครราชสีมา': 20494,  'เชียงใหม่': 20107,  
'กาญจนบุรี': 19483,  'ตาก': 16407,  
'อุบลราชธานี': 15774,  'สุราษฎร์ธานี': 12891}  
phuenthi['ชัยภูมิ'] = 12778  
phuenthi['แม่ฮ่องสอน'] = 12681  
print(phuenthi)  # ได้ {'นครราชสีมา': 20494, 'เชียงใหม่': 20107, 'กาญจนบุรี': 19483, 'ตาก': 16407, 'อุบลราชธานี': 15774, 'สุราษฎร์ธานี': 12891, 'ชัยภูมิ': 12778, 'แม่ฮ่องสอน': 12681}

หรืออาจใช้เมธอด update เพื่อรับเอาข้อมูลจากดิกชันนารีอื่นมาก็ได้

phuenthi.update({'เพชรบูรณ์': 12668,  'ลำปาง': 12534})  
print(phuenthi)  # ได้ {'นครราชสีมา': 20494, 'เชียงใหม่': 20107, 'กาญจนบุรี': 19483, 'ตาก': 16407, 'อุบลราชธานี': 15774, 'สุราษฎร์ธานี': 12891, 'ชัยภูมิ': 12778, 'แม่ฮ่องสอน': 12681, 'เพชรบูรณ์': 12668, 'ลำปาง': 12534}

ในการใช้ = นั้นจะเป็นการเพิ่มข้อมูลก็ต่อเมื่อคีย์ที่ใส่ไปนั้นไม่มีอยู่เดิม แต่หากคีย์ที่ใส่ไปนั้นมีอยู่ก่อนแล้วจะเป็นการแก้ค่าเขียนทับลงไปใหม่แทน ตัวอย่างการประยุกต์ใช้ ลองเขียนโปรแกรมสำหรับนับจำนวนตัวอักษรแต่ละตัวที่มีอยู่ในสายอักขระ

r =  "Si schiudono i boccioli color di rosa. L'ostinato inverno ha annunciato la propria fine."  # สายอักขระที่ต้องการวิเคราะห์  
nap = {}  # สร้างดิกชันนารีสำหรับเก็บค่าจำนวนของแต่ละตัวอักษร  
for  s  in  r.lower():  #เปลี่ยนให้เป็นตัวพิมพ์เล็กเพื่อจะนับรวมพิมพ์ใหญ่พิมพ์เล็กด้วยกัน  
if(s<'a'  or s>'z'):  continue  # เอาเฉพาะที่เป็นอักษรโรมัน a ถึง z ถ้าเจอสัญลักษณ์หรือช่องว่างให้ข้าม  
if(s  not in  nap):  # ถ้ายังไม่มีอยู่ในดิกชันนารี ให้ป้อนค่าใหม่โดยเริ่มนับ 1  
nap[s] = 1  
else:  # ถ้ามีอยู่ในดิกชันนารีแล้ว ให้แก้ค่าโดยบวกเพิ่มอีก 1  
nap[s] += 1  
print(nap)

(ที่มามาจากเนื้อเพลง)

ผลที่ได้

{'s': 4, 'i': 11, 'c': 5, 'h': 2, 'u': 2, 'd': 2, 'o': 12, 'n': 8, 'b': 1, 'l': 4, 'r': 5, 'a': 7, 't': 3, 'v': 1, 'e': 2, 'p': 2, 'f': 1}

หากต้องการให้แสดงผลโดยเรียงตามลำดับอักษรก็ใช้ sorted

for  q  in  sorted(nap):  
print('%s: %d'%(q,nap[q]))

หรือถ้าจะให้เรียงตามลำดับที่ปรากฏก็

for  q  in  sorted(nap, key=r.lower().index):  
print('%s: %d'%(q,nap[q]))

การใช้ for สร้างดิกชันนารี

เช่นเดียวกับลิสต์ ดิกชันนารีก็สามารถสร้างขึ้นจาก for ได้ ตัวอย่าง ลองใช้ดิกชันนารีเพื่อสร้างฟังก์ชันทางคณิตศาสตร์ขึ้นมา

f = {x:3*x+2  for  x  in  range(100)}  # สร้างฟังก์ชัน 3x+2 ที่มีโดเมนเป็นจำนวนเต็ม 0 ถึง 99  
print('f(3)=%d'%f[3])  # ได้ f(3)=11  
print('f(23)=%d'%f[23])  # ได้ f(23)=71  
print('f(99)=%d'%f[99])  # ได้ f(99)=299

ซึ่งการเขียนแบบนี้จะมีค่าเท่ากับการใช้ for เพื่อวนเพิ่มค่าไปทีละค่า

f = {}  # สร้างดิกชันนารีเปล่าขึ้นมาก่อน  
for  x  in  range(100):  
f[x] = 3*x+2  # ใส่เพิ่มไปทีละค่า

การลบข้อมูลในดิกชันนารี

สามารถลบทีละตัวได้โดยใช้เมธอด pop แล้วระบุคีย์ที่ต้องการลบ ตัวอย่าง ดิกชันนารีที่เก็บรหัสนักศึกษาและคะแนนวิชาภาษาไพธอนเบื้องต้น 1

python1 = {5966208114: 54, 5966100309: 59, 5966117558: 49,  
5966209223: 62, 5966195674: 48, 5966119902: 43}

สมมุติว่ามีนักศึกษาถอนวิชานี้ไปคนนึงเลยต้องลบข้อมูลออก

python1.pop(5966119902)  
print(python1)  # ได้ {5966208114: 54, 5966100309: 59, 5966117558: 49, 5966209223: 62, 5966195674: 48}

หรืออาจลบโดยใช้คำสั่ง del ก็ได้

del  ชื่อดิกชันนารี[คีย์ที่ต้องการลบ]

เช่น สมมุติว่านักศึกษาอีกคนก็ถอนวิชานี้

del  python1[5966195674]  
print(python1)  # ได้ {5966208114: 54, 5966100309: 59, 5966117558: 49, 5966209223: 62}

หากต้องการลบทั้งหมดให้ใช้เมธอด clear สมมุติว่านักศึกษาคนอื่นๆก็พากันถอนวิชานี้ออกหมด

python1.clear()  
print(python1)  # ได้ {}

ถ้าจะลบทั้งดิกชันนารีออกก็ใช้คำสั่ง del ตามด้วยชื่อดิกชันนารีโดยไม่ต้องใส่คีย์ สมมุติว่าพอคนถอนไปหมด อาจารย์ก็ลบข้อมูลของวิชานี้ทิ้งเสียเลย

del  python1  
print(python1)  # NameError: name 'python1' is not defined

คีย์หรือค่ามากกว่าหนึ่งตัว

บางครั้งค่าที่ต้องการอาจไม่ได้มีแค่ค่าเดียวแต่ต้องการเก็บสองค่าขึ้นไป กรณีแบบนี้สามารถใช้ค่าเป็นลิสต์หรือทูเพิลได้ ตัวอย่างเช่น บันทึกการเดินหมากในเกมโกะซึ่งตารางมีขนาด 19×19 แต่ละช่องแทนด้วยพิกัดในแกน x และ y

goban = {}  # สร้างดิกเปล่า แทนตารางโกะเปล่า  
goban['ดำ1'] = [16,4]  # ฝ่ายดำลงหมากตาที่ 1  
goban['ขาว1'] = [4,4]  
goban['ดำ2'] = [16,16]  
goban['ขาว2'] = [4,17]  
goban['ดำ3'] = [3,15]  
print(goban)

ผลหลังเดินไป ๕ ตา

{'ดำ1': [16, 4], 'ขาว1': [4, 4], 'ดำ2': [16, 16], 'ขาว2': [4, 17], 'ดำ3': [3, 15]}

ที่มาของหมากกระดานนี้ ศึก Google AlphaGo ปะทะ ฝานฮุย (樊麾)
https://www.youtube.com/watch?v=d9bXaLUPe9I

กรณีที่ต้องการคีย์สองตัวขึ้นไปก็สามารถทำได้เช่นกัน เพียงแต่คีย์ไม่สามารถใช้ลิสต์ได้ ต้องใช้ทูเพิลเท่านั้น เช่น ตัวอย่างที่แล้ว คราวนี้ใช้ตำแหน่งบนตารางเป็นคีย์

goban = {}  # สร้างดิกเปล่า แทนตารางโกะเปล่า  
goban[(16,4)] =  'ดำ1'  # ฝ่ายดำลงหมากตาที่ 1  
goban[(4,4)] =  'ขาว1'  
goban[(16,16)] =  'ดำ2'  
goban[(4,17)] =  'ขาว2'  
goban[(3,15)] =  'ดำ3'  
print(goban)

ผลหลังเดินไป ๕ ตา

{(16, 4): 'ดำ1', (4, 4): 'ขาว1', (16, 16): 'ดำ2', (4, 17): 'ขาว2', (3, 15): 'ดำ3'}

กรณีนี้จะสะดวกเวลาที่จะค้นว่าตำแหน่งไหนมีการลงหมากไปหรือยังและเป็นหมากอะไรที่ลงไว้ เช่น

x,y =  int(input()),int(input())  # ป้อนค่าตำแหน่ง  
if((x,y)  in  goban.keys()):  
print(goban[(x,y)])  # ถ้ามีหมากอยู่ก็จะบอกว่าเป็นหมากอะไร  
else:  
print('ว่างเปล่า')  # ถ้าไม่มีก็จะบอกว่าว่างเปล่า

ดิกชันนารีซ้อนดิกชันนารี

ค่าภายในดิกชันนารีนอกจากจะใช้ทูเพิลหรือลิสต์ได้แล้วก็ยังสามารถใช้เป็นดิกชัน นารีย่อยข้างในได้อีกด้วย พอซ้อนกันหลายชั้นพอเรียกใช้ก็ต้องใส่ [ ] ซึ่งมีคีย์ใส่อยู่ซ้อนกัน
ตัวอย่าง ลองสร้างดิกชันนารีเก็บข้อมูลของโปเกมอนจำนวน ๔ ตัว แต่ละตัวใช้ชื่อเป็นคีย์ พอค้นแล้วจะได้ค่าซึ่งเป็นดิกชันนารีที่ประกอบด้วย หมายเลข, ส่วนสูง, น้ำหนัก, และ ชนิด

khomun = {'ฟุชิงิดาเนะ': {'หมายเลข':  '001','ส่วนสูง': 0.7,'น้ำหนัก': 6.9,'ชนิด':['พืช','พิษ']},  
'ฮิโตคาเงะ': {'หมายเลข':  '004','ส่วนสูง': 0.6,'น้ำหนัก': 8.5,'ชนิด':['ไฟ']},  
'เซนิงาเมะ': {'หมายเลข':  '007','ส่วนสูง': 0.5,'น้ำหนัก': 9.0,'ชนิด':['น้ำ']},  
'พีคาชู': {'หมายเลข':  '025','ส่วนสูง': 0.4,'น้ำหนัก': 6.0,'ชนิด':['ไฟฟ้า']}}

เวลาเข้าถึงค่าภายในก็ เช่น

print(khomun['พีคาชู']['น้ำหนัก'])  # ได้ 6.0  
print(khomun['ฟุชิงิดาเนะ']['ชนิด'])  # ได้ ['พืช', 'พิษ']  
print(khomun['ฮิโตคาเงะ']['หมายเลข'])  # ได้ 004

สามารถใช้ for เพื่อแสดงค่าทั้งหมด

for  pk  in  sorted(khomun):  
print('===='+pk+'====')  # แสดงชื่อ  
for  s  in  khomun[pk]:  
if(s!='ชนิด'):  print(s+': %s'%khomun[pk][s])  # แสดงหมายเลข, น้ำหนัก, ส่วนสูง  
else:  print(s+': '+' + '.join(khomun[pk][s]))  # แสดงชนิด

ผลลัพธ์

====พีคาชู====  
หมายเลข: 025  
ส่วนสูง: 0.4  
น้ำหนัก: 6.0  
ชนิด: ไฟฟ้า  
====ฟุชิงิดาเนะ====  
หมายเลข: 001  
ส่วนสูง: 0.7  
น้ำหนัก: 6.9  
ชนิด: พืช + พิษ  
====ฮิโตคาเงะ====  
หมายเลข: 004  
ส่วนสูง: 0.6  
น้ำหนัก: 8.5  
ชนิด: ไฟ  
====เซนิงาเมะ====  
หมายเลข: 007  
ส่วนสูง: 0.5  
น้ำหนัก: 9.0  
ชนิด: น้ำ

สรุปเนื้อหา

ดิกชันนารีเป็นข้อมูลอีกประเภทหนึ่งที่สามารถนำมาประยุกต์ใช้งานได้มากเช่น เดียวกับลิสต์, ทูเพิล และเซ็ต หากแยกใช้ข้อมูลแต่ละอย่างตามความเหมาะสมของงานแล้วก็จะสามารถทำงานอะไร ต่างๆได้อย่างมีประสิทธิภาพ

อ้างอิง http://www.python-izm.com/contents/basis/dict.shtml
http://www.pythonweb.jp/tutorial/dictionary
http://www.tohoho-web.com/python/list.html

Reference : https://phyblas.hinaboshi.com/tsuchinoko14