แม้ว่าในไพธอนจะมีฟังก์ชันสำหรับจัดการไฟล์อยู่แล้ว แต่เพื่อที่จะจัดการบางอย่างที่จำเพาะสำหรับอาเรย์แล้ว numpy มีคำสั่งสำหรับทำตรงนี้โดยเฉพาะ
การเปิดไฟล์ข้อความเพื่อสร้างอาเรย์ สมมุติว่ามีไฟล์ชื่อ 123456789.txt เขียนอะไรแบบนี้ไว้
1 2 3
4 5 6
7 8 9
หากต้องการให้เลขเหล่านี้กลายมาเป็นอาเรย์ ถ้าใช้ฟังก์ชันมาตรฐานของไพธอนจะเขียนแบบนี้
import numpy as np
f = open('123456789.txt','r')
m = f.readlines() # อ่านข้อความในไฟล์โดยแยกเป็นแต่ละแถวไว้ในลิสต์
a = [x.split() for x in m] # ใช้ split เพื่อแยกข้อความด้านในเป็นลิสต์ย่อย
ari = np.array(a,dtype=float) # นำมาเปลี่ยนแป็นอาเรย์ โดยกำหนดชนิดให้ด้วยไม่เช่นนั้นจะเป็นสายอักขระ
f.close()
(รายละเอียดเกี่ยวกับเรื่องการเปิดอ่านไฟล์แบบทั่วไปอ่านได้ในเนื้อหา
ภาษาไพธอนเบื้องต้นบทที่ ๑๗)
ซึ่งก็จะเห็นว่าประกอบไปด้วยหลายขั้นตอนกว่าจะมาเป็นอาเรย์ แต่หากเรารู้อยู่แล้วว่าเราต้องการเปิดไฟล์ขึ้นมาเพื่อสร้างอาเรย์ละก็ กรณีแบบนี้ใช้คำสั่ง np.loadtxt ของ numpy จะเร็วกว่า
หากใช้ np.loadtxt สามารถเขียนใหม่ได้เป็น
ari = np.loadtxt('123456789.txt')
เท่านี้ก็จะได้ผลลัพธ์แบบเดียวกันด้วยการเขียนแค่บรรทัดเดียว
แต่ทีนี้บางทีมันก็ไม่ง่ายเช่นนั้น เพราะตัวเลขอาจจะไม่ได้ถูกกั้นด้วยช่องว่าง แต่อาจใช้อะไรบางอย่างเช่นจุลภาคเป็นต้น เช่น
1, 2, 3
4, 5, 6
7, 8, 9
กรณีแบบนั้นจำเป็นต้องเพิ่มคีย์เวิร์ด delimiter เข้าไป
ari = np.loadtxt('123456789.txt',delimiter=',')
ในบางครั้งภายในไฟล์ก็อาจไม่ได้มีแต่ข้อมูลที่เราต้องการ แต่มีการจั่วหัว เช่น
sawatdi rao chue array
x, y, z
1, 2, 3
4, 5, 6
7, 8, 9
กรณีแบบนี้ต้องเติมคีย์เวิร์ด skiprows โดยใส่ค่าเท่ากับจำนวนบรรทัดที่ต้องการข้าม เพื่อใหัมันโดดข้ามไปที่บรรทัดที่ต้องการอ่านจริงๆ
ari = np.loadtxt('123456789.txt',delimiter=',',skiprows=2)
เพียงแต่ว่าในกรณีที่ในไฟล์มีตัวอักษรที่ไม่ใช่ ascii ปนอยู่ด้วย เช่น
สวัสดี เราชื่ออาเรย์
x, y, z
1, 2, 3
4, 5, 6
7, 8, 9
แบบนี้มันจะไม่สามารถอ่านได้ จำเป็นต้องเปิดไฟล์ด้วย open แล้วจึงใช้ไฟล์นั้นมาใส่ใน np.loadtxt อีกที จึงเป็นแบบนี้
f = open('123456789.txt',encoding='utf-8')
ari = np.loadtxt(f,delimiter=',',skiprows=2)
นั่นเป็นวิธีจัดการกับข้อความส่วนบรรทัดอื่นที่ไม่เกี่ยวข้อง แต่หากข้อความที่ไม่เกี่ยวข้องนั้นอยู่ในบรรทัดเดียวกับข้อมูลก็จะต้องใช้ usecols
usecols เป็นคีย์เวิร์ดที่ใช้กำหนดว่าเราจะเอาข้อมูลจากแถวไหนที่อ่านได้ไปบ้าง โดยแถวแรกนับเป็น 0
ตัวอย่างเช่นข้อความเป็นแบบนี้
o, x, y, z
a, 1, 2, 3
b, 4, 5, 6
c, 7, 8, 9
ก็ต้องเขียนแบบนี้
ari = np.loadtxt('123456789.txt',delimiter=',',skiprows=1,usecols=[1,2,3])
แบบนี้ a b c ก็จะไม่ถูกนำมารวมในอาเรย์ที่โหลดขึ้นมาด้วย
ถ้ากำหนดคีย์เวิร์ด unpack=1 จะทำให้อาเรย์ที่ได้ถูกทรานสโพสสลับแกนไป
ari = np.loadtxt('123456789.txt',delimiter=',',skiprows=1,usecols=[1,2,3],unpack=1)
ผลลัพธ์
[[ 1. 4. 7.]
[ 2. 5. 8.]
[ 3. 6. 9.]]
ปกติถ้าเราไม่ได้กำหนดชนิดข้อมูลเอาไว้อาเรย์ที่สร้างขึ้นจะมีสมาชิกเป็น float ทั้งหมด แต่ก็สามารถกำหนดชนิดข้อมูลได้ตามต้องการด้วยคีย์เวิร์ด dtype เช่นเดียวกับตอนที่สร้างอาเรย์ธรรมดา
เช่นถ้าต้องการจำนวนเต็ม
ari = np.loadtxt('123456789.txt',delimiter=',',skiprows=1,usecols=[1,2,3],dtype=int)
ได้
[[1 2 3]
[4 5 6]
[7 8 9]]
สรุปคีย์เวิร์ดทั้งหมด
delimiter |
ตัวที่ใช้แบ่งสมาชิกในแต่ละหลัก |
usecols |
กำหนดว่าจะใช้หลักไหนบ้าง |
unpack |
กำหนดว่าอาเรย์ที่ได้จะทรานสโพสหรือเปล่า |
dtype |
ชนิดของข้อมูลอาเรย์ |
skiprows |
กำหนดว่าจะข้ามไปกี่แถว |
การบันทึกอาเรย์ ในทางตรงกันข้ามกับการอ่านอาเรย์ คำสั่งที่ใช้บันทึกอาเรย์คือ savetxt
arai = np.arange(1,9).reshape(2,4)
np.savetxt('12345678.txt',arai)
พอเปิดไฟล์ก็จะได้เป็นแบบนี้
1.000000000000000000e+00 2.000000000000000000e+00 3.000000000000000000e+00 4.000000000000000000e+00
5.000000000000000000e+00 6.000000000000000000e+00 7.000000000000000000e+00 8.000000000000000000e+00
นั่นเพราะเราไม่ได้กำหนดรูปแบบการแสดงผลมันก็เลยออกมาเป็นแบบนี้ รูปแบบการแสดงผลสามารถระบุได้โดยใส่คีย์เวิร์ด fmt โดยค่าที่ใส่ก็คือพวก %d %f อ่านรายละเอียดได้ในเนื้อหาภาษาไพธอนเบื้องต้น
บทที่ ๑๐ คีย์เวิร์ด fmt อยู่ในตำแหน่งที่ ๓ อยู่แล้ว ดังนั้นอาจใส่เป็นอาร์กิวเมนต์ตัวที่ ๓ ไปโดยไม่ต้องพิมพ์ fmt= ก็ได้
ลองแก้เป็น
np.savetxt('12345678.txt',arai,'%05d')
จะได้
00001 00002 00003 00004
00005 00006 00007 00008
สามารถกำหนดตัวคั่นได้ด้วยคีย์เวิร์ด delimiter เช่นเดียวกับตอนอ่าน
หากต้องการให้เปิดอ่านในไมโครซอฟต์เอ็กซ์เซลเป็นตารางได้เลยก็อาจคั่นด้วย , และนิยมเซฟไฟล์เป็น .csv
np.savetxt('12345678.csv',arai,'%d',delimiter=',')
หากต้องการให้ข้อมูลถูกบันทึกลงบรรทัดเดียวกันไปเลยโดยไม่ต้องขึ้นบรรทัดใหม่ก็ ให้ใส่คีย์เวิรืด newline ใส่ค่าเป็นอะไรก็ได้ที่ต้องการให้เป็นตัวคั่นในแต่บรรทัด
ตัวอย่าง
np.savetxt('12345678.txt',arai,'%d',delimiter='_',newline='||')
จะได้
1_2_3_4||5_6_7_8||
นอกจากนี้ยังสามารถเพิ่มข้อความที่ไม่เกี่ยวข้องลงไปด้วยได้โดยใส่คีย์เวิร์ด header และ footer ซึ่งจะแทรกข้อความที่หัวและท้ายตามลำดับ
ลอง
np.savetxt('12345678.txt',arai,'%d',header='header',footer='footer')
จะได้เป็น
# header
1 2 3 4
5 6 7 8
# footer
จะเห็นว่าข้อความเป็นไปตามที่พิมพ์แต่มีเพิ่ม # เข้ามาด้านหน้า หากเราเอาไฟล์นี้ไปเปิดด้วยคำสั่ง np.loadtxt จะพบว่ามันไม่อ่านตรงนี้โดยที่เราไม่จำเป็นต้องใส่คีย์เวิร์ด skiprows ด้วย
แต่หากเราไม่ต้องการให้มี # นำหน้าหรืออยากเปลี่ยนเป็นอย่างอื่นก็ต้องเพิ่มคีย์เวิร์ดไปอีกตัวคือ comments
np.savetxt('12345678.txt',arai,'%d',header='header',footer='footer',comments='!!!')
ผลที่ได้ก็จะเป็น
!!!header
1 2 3 4
5 6 7 8
!!!footer
สรุปคีย์เวิร์ดทั้งหมด
fmt |
รูปแบบตัวหนังสือที่จะเขียน |
delimiter |
ตัวคั่นระหว่างหลัก ถ้าไม่ใส่จะเป็นการเว้นช่องว่างหนึ่งช่อง |
newline |
ตัวคั่นระหว่างแถว ถ้าไม่ใส่จะเป็น \n |
header |
ข้อความส่วนหัว |
footer |
ข้อความส่วนท้าย |
comments |
ข้อความที่นำหน้าข้อความส่วนหัวและท้าย ถ้าไม่ใส่จะเป็น # |
การบันทึกเป็นไฟล์เฉพาะสำหรับ numpy นอกจากการจัดการกับไฟล์ทั่วไปดังที่กล่าวไปแล้ว numpy ยังมีวิธีการบันทึกเป็นไฟล์ในรูปแบบของตัวเองด้วย ทำได้โดยใช้ฟังก์ชัน np.save
ไฟล์ของ numpy นั้นมีชื่อสกุลเป็น .npy เวลาที่เซฟเราต้องพิมพ์ชื่อไฟล์ให้เป็น .npy ไม่เช่นนั้น .npy จะถูกเติมต่อท้ายให้โดยอัตโนมัติ
ตัวอย่าง
aay = np.arange(1,5).reshape(2,2)
np.save('1234',aay)
จากนั้นก็จะได้ไฟล์ชื่อ 1234.npy มา ซึ่งเราจะไม่สามารถเปิดอ่านดูภายในได้เพาะเป็นไบนารี
การเปิดอ่านไฟล์ทำได้โดยใช้ฟังก์ชัน np.load
yaa = np.load('1234.npy')
หากต้องการบันทึกอาเรย์หลายตัวไว้ในไฟล์เดียวก็สามารถทำได้ด้วยคำสั่น np.savez
(นอกเรื่อง) คำว่า savez เป็นภาษาฝรั่งเศส เป็นรูปผันของคำว่า savoir แปลว่า "รู้" ใช้กับประธานบุรุษที่สองพหูพจน์ในกาลปัจจุบัน
การใช้ฟังก์ชันนี้ให้ใส่อาเรย์ที่ต้องการบันทึกอยู่เป็นอาร์กิวเมนต์ตัวถัดจากชื่อไฟล์ได้เลย ใส่กี่ตัวก็ได้ตามลำดับ
ไฟล์ที่ได้จากคำสั่งนี้จะมีชื่อสกุลเป็น npz
ตัวอย่าง
axa = np.arange(1,5)
aya = np.arange(11,15)
aza = np.arange(101,105)
np.savez('aaa',axa,aya,aza)
จะได้ไฟล์ชื่อ aaa.npz มา ซึ่งไม่สามารถเปิดอ่านได้โดยตรง แต่ว่าอาเรย์ทั้งหมดก็ได้ถูกเก็บอยู่ในนั้นแล้ว
จากนั้นก็เปิดอ่านขึ้นมาได้ด้วยฟังก์ชัน np.load เหมือนกับอาเรย์เดี่ยว แต่ว่าออบเจ็กต์ที่ได้มาจะอยู่ในรูป numpy.lib.npyio.NpzFile ซึ่งยังเข้าถึงอาเรย์แต่ละตัวไม่ได้ในทันที จะเข้าถึงได้ในลักษณะการเขียนเป็นแบบดิกชันนารี โดยที่ชื่อจะเป็น arr_0, arr_1, arr_2 ตามลำดับที่ใส่เข้าไป
ตัวอย่าง
awa = np.load('aaa.npz')
print(awa['arr_0']) # ได้ [1 2 3 4]
print(awa['arr_1']) # ได้ [11 12 13 14]
print(awa['arr_2']) # ได้ [101 102 103 104]
แต่ว่าเราสามารถตั้งชื่อให้กับอาเรย์ที่ใส่เข้าไปได้ตามที่ต้องการได้ โดยใส่ในรูปของคีย์เวิร์ด แล้วคีย์เวิร์ดนั้นจะกลายมาเป็นชื่อ
axa = np.arange(1,5)
aya = np.arange(11,15)
aza = np.arange(101,105)
np.savez('aaa',xa=axa,ya=aya,za=aza)
awa = np.load('aaa.npz')
print(awa['xa']) # ได้ [1 2 3 4]
เราสามารถตรวจดูชื่ออาเรย์ทั้งหมดที่เก็บอยู่ในไฟล์นั้นได้โดยดูที่แอตทริบิวต์ files
awa = np.load('aaa.npz')
print(awa.files) # ได้ ['ya', 'za', 'xa']
อ้างอิง