φυβλαςのβλογ
บล็อกของ phyblas



ใช้ h5py เพื่อบันทึกอาเรย์ numpy เป็นไฟล์ hdf5
เขียนเมื่อ 2018/01/04 23:52
แก้ไขล่าสุด 2021/09/28 16:42
ในเนื้อหา numpy บทที่ 39 ได้อธิบายถึงการบันทึกอาเรย์ลงไฟล์ https://phyblas.hinaboshi.com/numa39

แต่ว่านอกจากวิธีนั้นแล้วยังมีอีกวิธีที่น่าสนใจ นั่นคือบันทึกในรูปแบบไฟล์ hdf5

hdf คือรูปแบบการจัดเก็บไฟล์แบบไบนารีชนิดหนึ่ง ปัจจุบันใช้รุ่นที่เรียกว่า hdf5 ชื่อสกุลไฟล์มักเขียนเป็น .h5

สามารถใช้ในไพธอนได้โดยใช้มอดูล hdf5 ซึ่งสามารถติดตั้งได้ง่ายด้วย pip
pip install h5py

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

การใช้งานจะคล้ายกับการใช้คำสั่ง np.savez ของ numpy คือใช้เซฟหลายอาเรย์ลงในไฟล์เดียว แต่มีวิธีการเขียนที่ยุ่งยากกว่าหน่อย

เริ่มจากการบันทึกอาเรย์ลงไฟล์ เขียนได้ดังนี้
import h5py
import numpy as np

xx = np.random.random(1000000)
yy = np.random.random([1000,1000])
h5f = h5py.File('hy.h5','w')
h5f.create_dataset('x',data=xx)
h5f.create_dataset('y',data=yy)
h5f.close()

ขั้นตอนเริ่มจากเปิดไฟล์ขึ้นมาด้วย h5py.File จากนั้นก็ทำการบันทึกไฟล์ โดยปิดท้ายจะต้องปิดไฟล์ด้วยเมธอด .close ด้วย คล้ายกับคำสั่งเปิดปิดไฟล์ด้วย open ซึ่งเป็นฟังก์ชันพื้นฐานของไพธอน (รายละเอียด https://phyblas.hinaboshi.com/tsuchinoko17)

ดังนั้นจะเขียนในรูป with ก็ได้เช่นกัน เพื่อความสะดวกไม่ต้องพะวงเรื่องปิดไฟล์ตอนท้าย ดังนั้น ๔ บรรทัดสุดท้ายนั้นอาจเขียนแทนด้วย ๓ บรรทัดตามนี้
with h5py.File('hy.h5','w') as h5f:
    h5f.create_dataset('x',data=xx)
    h5f.create_dataset('y',data=yy)

อาร์กิวเมนต์ตัวแรกคือที่อยู่และชื่อของไฟล์ ส่วนตัวที่ ๒ คือชนิดของการเปิดไฟล์ ในที่นี้เปิดเพื่อเขียน ดังน้นคือ w

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



จากนั้นเวลาเปิดอ่านไฟล์ก็ทำในลักษณะคล้ายกัน คือใช้ h5py.File เหมือนกัน แต่เลือกโหมดเป็น r หรือจะไม่ใส่ r ก็ได้เพราะ r คือค่าตั้งต้นอยู่แล้ว

เวลาที่อ่านข้อมูลจากไฟล์ก็เปิดในลักษณะเช่นเดียวกับดิกชันนารี คือใส่ชื่อของชุดข้อมูล แต่จะต้องต่อด้วย [:] ลงไปด้วย ไม่เช่นนั้นข้อมูลจะยังไม่ถูกอ่านเข้าไปในตัวแปรทันที

เขียนได้ดังนี้
h5f = h5py.File('hy.h5')
xx = h5f['x'][:]
yy = h5f['y'][:]
h5f.close()


สำหรับเรื่องความเร็วนั้นลองมาทดสอบกันดู

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

ในที่นี้มี ๔ วิธี คือใช้ np.save np.savez h5py และ fits สกุลไฟล์ คือ .npy .npz .h5 และ .fits

คำสั่ง np.save จะบันทึกแยกไฟล์ ไฟล์ละอาเรย์ ส่วน np.savez จะเป็นการบันทึกรวมกี่อาเรย์ก็ได้ในไฟล์เดียว เช่นเดียวกับ h5

ส่วน fits เป็นไฟล์ข้อมูลไบนารีรูปแบบนึง สามารถบันทึกและเปิดได้ผ่านมอดูล astropy แต่ในที่นี้จะไม่อธิบายรายละเอียด แค่จะยกมาใช้เทียบเฉยๆ

เขียนได้ดังนี้ สามารถดูแล้วเปรียบเทียบลักษณะการเขียนของแต่ละวิธีไปด้วยได้
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import time,h5py,os
from astropy.io import fits

chuefile = 'xy'

def saveh5f(x,y,n):
    h5f = h5py.File(chuefile+'%d.h5'%n,'w')
    h5f.create_dataset('x',data=x)
    h5f.create_dataset('y',data=y)
    h5f.close()

def savenpy(x,y,n):
    np.save(chuefile+'_x%d.npy'%n,x)
    np.save(chuefile+'_y%d.npy'%n,y)

def savenpz(x,y,n):
    np.savez(chuefile+'%d.npz'%n,x=x,y=y)

def savefits(x,y,n):
    if(os.path.isfile(chuefile+'%d.fits'%n)): os.remove(chuefile+'%d.fits'%n)
    fits.HDUList([fits.PrimaryHDU(x),fits.ImageHDU(y)]).writeto(chuefile+'%d.fits'%n)



for f in [saveh5f,savenpy,savenpz,savefits]:
    ttt = []
    ttt_std = []
    nnn = 10**np.arange(3,9)
    for n in nnn:
        x = np.random.random(n)
        y = np.random.random(int(n/10))
        tt = []
        for i in range(5):
            t1 = time.time()
            f(x,y,n)
            tt.append(time.time()-t1)
        ttt.append(tt)
    ttt = np.log10(np.array(ttt))
    std = np.array(ttt).std(1)
    mean = np.array(ttt).mean(1)
    nnn = np.log10(nnn)
    plt.plot(nnn,mean)
    plt.fill_between(nnn,mean-std,mean+std,alpha=0.3)

plt.xlabel('log(จำนวนข้อมูล)',family='Tahoma')
plt.ylabel('log(เวลา)',family='Tahoma')
plt.legend(['h5','npy','npz','fits'])
plt.show()



จากผลการวัดจะเห็นได้ว่าใช้ h5 จะช้ากว่าใช้ npy ถ้าขนาดอาเรย์เล็ก แต่จะกลายเป็นเร็วพอๆกันขึ้นมาเมื่อขนาดอาเรย์ใหญ่

แต่ h5 จะสะดวกกว่าเพราะเก็บอาเรย์ทั้งหมดลงในไฟล์เดียว เหมือนกับ npz แต่ว่าหากใช้ npz จะช้ากว่า ดังนั้นใช้ h5 จึงคุ้ม



จากนั้นลองเปรียบเทียบตอนเปิดอ่านข่้อมูลดู
def loadh5f(n):
    with h5py.File(chuefile+'%d.h5'%n,'r') as h5f:
        x = h5f['x'][:]
        y = h5f['y'][:]
    return x,y

def loadnpy(n):
    x = np.load(chuefile+'_x%d.npy'%n)
    y = np.load(chuefile+'_y%d.npy'%n)
    return x,y

def loadnpz(n):
    xy = np.load(chuefile+'%d.npz'%n)
    return xy['x'],xy['y']

def loadfits(n):
    hdulist = fits.open(chuefile+'%d.fits'%n)
    return hdulist[0].data,hdulist[1].data

for f in [loadh5f,loadnpy,loadnpz,loadfits]:
    ttt = []
    ttt_std = []
    nnn = 10**np.arange(3,9)
    for n in nnn:
        tt = []
        for i in range(5):
            t1 = time.time()
            x,y = f(n)
            tt.append(time.time()-t1)
        ttt.append(tt)
    ttt = np.log10(np.array(ttt))
    std = np.array(ttt).std(1)
    mean = np.array(ttt).mean(1)
    nnn = np.log10(nnn)
    plt.plot(nnn,mean)
    plt.fill_between(nnn,mean-std,mean+std,alpha=0.3)

plt.xlabel('log(จำนวนข้อมูล)',family='Tahoma')
plt.ylabel('log(เวลา)',family='Tahoma')
plt.legend(['h5','npy','npz','fits'])
plt.show()


ผลที่ได้จะเห็นว่า h5 ช้ากว่า npy เฉพาะตอนอาเรย์เล็ก แต่เร็วพอๆกันตอนอาเรย์ใหม่ และ npz ก็ช้าสุดอีกเช่นกัน

แต่ที่เร็วสุดกลับเป็น fits ซึ่งไม่ว่าข้อมูลจะใหญ่แค่ไหนก็ใช้เวลาไม่ค่อยต่างกัน



โดยสรุปก็คือ ข้อดีของการใช้ h5py ก็คือสามารถบันทึกและเปิดไฟล์ที่ประกอบด้วยอาเรย์ทีละหลายตัวได้เหมือนกับ np.savez แต่มีความเร็วเท่ากับ np.save ดังนั้นเหมาะที่จะใช้แทน np.savez



อ้างอิง


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> numpy

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文