ใน
บทที่ ๑๓ ได้พูดถึงการสร้างฮิสโทแกรมไป ฮิสโทแกรมนั้นมีไว้สำหรับแสดงการแจกแจงความหนาแน่นของอะไรบางอย่างในหนึ่งมิติ
การแสดงการแจกแจงในหนึ่งมิติเราต้องสร้างแผนภาพเป็นสองมิติ ดังนั้นหากต้องการแจกแจงในสองมิติ ผลที่ได้ก็จะเป็นแผนภาพสามมิติ
แต่ในที่นี้เราจะแสดงมิติที่สามในรูปของสี นั่นคือจะสร้างแผนภาพไล่สีขึ้น
การสร้างแผนภาพไล่สีแสดงการแจกแจงความหนาแน่น matplotlib มีคำสั่งสำหรับสร้างแผนภาพไล่สีแสดงการแจกแจงความหนาแน่น นั่นคือฟังก์ชัน hist2d
ตัวอย่างการใช้ ลองให้แสดงภาพการแจกแจงของตำแหน่งที่สุ่มขึ้นด้วยการแจกแจงแบบปกติ
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(1000000)-2
x[500000:] += 4 # ให้จุดจำนวนครึ่งหนึ่งมีตำแหน่งในแกน x เลื่อนห่างไป
y = np.random.randn(1000000)
plt.hist2d(x,y,bins=100,cmap='coolwarm')
plt.colorbar(pad=0.01)
plt.subplots_adjust(0.05,0.05,1.05,0.95)
plt.show()
คีย์เวิร์ด bins ก็คือจำนวนช่อง คล้ายกับในฮิสโทแกรมหนึ่งมิติ
เราสามารถกำหนดจำนวนช่องในแนวตั้งและนอนให้ไม่เท่ากันได้โดยใส่ค่าเป็นคู่อันดับ (ทูเพิลหรือลิสต์)
ลองแก้เป็น
plt.hist2d(x,y,bins=[20,100],cmap='coolwarm')
แบบนี้แนวตั้งจะถูกแบ่งละเอียด แต่แนวนอนจะถูกแบ่งหยาบ
ถ้าให้แกนหนึ่งเป็น 1 ก็สามารถทำเป็นการแจกแจงในแกนเดียวได้
plt.hist2d(x,y,bins=[500,1],cmap='coolwarm')
หรือจะใช้เป็นลิสต์หรืออาเรย์ของค่าที่ต้องการจะแบ่ง เช่นลองใช้ np.linspace เพื่อสร้างลิสต์ของค่าจุดแบ่ง
plt.hist2d(x,y,bins=[np.linspace(-6,6,900),np.linspace(-4,4,200)],cmap='coolwarm')
ดังนั้นแบบนี้จะแบ่งช่องให้ไม่เท่ากันก็สามารถทำได้ เพียงแต่ว่าการแบ่งช่องไม่เท่ากันจะทำให้เราตีความความหนาแน่นผิดเพี้ยน เนื่องจากช่องที่ถูกแบ่งแคบจะมีค่าน้อย
ตัวอย่าง
plt.hist2d(x,y,bins=[np.linspace(-1.5,1.5,40)**3,np.linspace(-1.5,1.5,40)**3],cmap='coolwarm')
ค่าการแจกแจงที่เห็นอยู่นี้เป็นไปตามจำนวนที่อยู่ในแต่ละช่วงจริงๆ แต่หากต้องการให้คิดเป็นสัดส่วนก็เพิ่มคีย์เวิร์ด normed=1 (หรือ normed=True) ลงไป ค่าที่ปรากฏในแถบสีจะเป็นค่าที่หารด้วยจำนวนทั้งหมดแล้ว
ตัวอย่าง ลองแก้เป็น
plt.hist2d(x,y,bins=100,normed=1,cmap='coolwarm')
นอกจากนี้ส่วนใหญ่คีย์เวิร์ดที่ใช้ได้ก็จะเหมือนกับกราฟแจกแจงทั่วไป
จะทำเป็นกราฟล็อการิธึมก็ได้โดยใส่คีย์เวิร์ด norm
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
x = np.random.randn(1000000)-2
x[500000:] += 4
y = np.random.randn(1000000)
plt.hist2d(x,y,bins=100,norm=mpl.colors.LogNorm())
plt.colorbar(pad=0.01)
plt.subplots_adjust(0.05,0.05,1.05,0.95)
plt.show()
ที่เห็นส่วนที่เป็นสีขาวนั่นคือมีค่าการแจกแจงเป็น 0 ซึ่งไม่สามารถหาค่าลอการิธึมได้
อนึ่ง norm กับ normed เป็นคีย์เวิร์ดคนละตัวกัน ต้องระวังอย่าสับสน
การแจกแจงเป็นช่องหกเหลี่ยม นอกจากจะให้แจกแจงเป็นจุดๆช่องสี่เหลี่ยมธรรมดาแล้ว หากต้องการแบ่งเป็นช่องหกเหลี่ยมก็สามารถทำได้ matplotlib มีฟังก์ชัน plt.hexbin เพื่อใช้ในการนี้
ตัวอย่าง
x = np.random.randn(1000000)-2
x[500000:] += 4
y = np.random.randn(1000000)
plt.hexbin(x,y,gridsize=20,cmap='coolwarm')
plt.colorbar(pad=0.01)
plt.subplots_adjust(0.05,0.05,1,0.95)
plt.show()
จำนวนเส้นที่แบ่งกำหนดโดยคีย์เวิร์ด gridsize จะให้สองแกนแบ่งไม่เท่ากันก็ทำได้ด้วยการใส่เป็นคู่อันดับเช่นกัน
plt.hexbin(x,y,gridsize=[5,30],cmap='coolwarm')
ขอบเขตของการแสดงผลการกระจายสามารถกำหนดด้วยคีย์เวิร์ด extent
plt.hexbin(x,y,gridsize=[20,30],extent=[-10,10,-5,5],cmap='coolwarm')
การเอาข้อมูลจากแผนภาพการแจกแจง เวลาที่แผนภาพไล่สีแสดงความหนาแน่นถูกสร้างขึ้นมานั้น มันจะต้องมีการคำนวณค่าความหนาแน่นของแต่ละจุดขึ้นมาจากข้อมูลที่เราป้อนให้ ข้อมูลนั้นไม่ได้หายไปไหนหลังจากที่ภาพถูกวาดขึ้นมา และเราก็สามารถนำข้อมูลตรงนั้นมาใช้ทำอย่างอื่นต่อได้อีกด้วย
หากเราเอาตัวแปรมารับฟังก์ชัน hist2d จะพบว่าได้ค่าคืนกลับมาเป็นทูเพิลที่มีสมาชิกอยู่ ๔ ตัว ซึ่งก็คือ
- ค่าแสดงความหนาแน่นของแต่ละจุด อยู่ในรูปอาเรย์สองมิติ
- อาเรย์รวมค่าจุดขีดแบ่งในแกน x
- อาเรย์รวมค่าจุดขีดแบ่งในแกน y
- ออบเจ็กต์ของตัวฮิสโทแกรม
ตัวอย่าง
x = np.random.randn(1000000)-2
x[500000:] += 4
y = np.random.randn(1000000)
h = plt.hist2d(x,y,bins=100,cmap='hsv')
plt.colorbar()
plt.show()
print(h[0].shape) # ได้ (100, 100)
print('%.3f,%.3f'%(h[0].min(),h[0].max())) # ได้ 0.000,1061.000
print(h[0].sum()) # ได้ 1000000.0
print(h[1].shape) # ได้ (101,)
print('%.3f,%.3f'%(h[1].min(),h[1].max())) # ได้ -6.549,6.384
print(h[2].shape) # ได้ (101,)
print('%.3f,%.3f'%(h[2].min(),h[2].max())) # ได้ -4.882,4.894
print(h[3]) # ได้ AxesImage(80,52.8;396.8x369.6)
เราสามารถเอาข้อมูลที่ได้จากตรงนี้มาลองวาดเป็นกราฟแสดงค่าการแจกแจงต่ออีกทีได้
x = (h[1][1:]+h[1][:-1])/2
y = (h[2][1:]+h[2][:-1])/2
zx = h[0].sum(axis=1)
zy = h[0].sum(axis=0)
plt.subplot(2,1,1)
plt.plot(x,zx)
plt.subplot(2,1,2)
plt.plot(y,zy)
plt.show()
การใช้ histogram2d ของ numpy จะเห็นว่า plt.hist2d ทำให้เราได้ค่าความหนาแน่นการกระจายออกมาได้ แต่หากเราแค่ต้องการค่าความหนาแน่นโดยที่ไม่ต้องมาวาดกราฟแล้วละก็ ใน numpy เองก็มีคำสั่งที่ใช้ทำได้อยู่แล้ว นั่นคือ np.histogram2d
การใช้ np.histogram2d นั้นจะคล้ายกับ plt.hist2d เพียงแต่ np.histogram2d จะได้แค่ค่าคืนกลับแต่ไม่ได้วาดกราฟออกมา และค่าคืนกลับที่ได้ก็จะมีแค่ ๓ ตัว ซึ่งก็คือสามตัวแรกของ np.histogram2d
ตัวอย่าง ลองใช้ np.histogram2d กับ plt.hist2d เทียบกัน
x = np.random.randn(1000000)-2
x[500000:] += 4
y = np.random.randn(1000000)
hisa = np.histogram2d(x,y,bins=[150,100])
his = plt.hist2d(x,y,bins=[150,100])
print(np.all(hisa[0]==his[0])) # ได้ True
print(np.all(hisa[1]==his[1])) # ได้ True
print(np.all(hisa[2]==his[2])) # ได้ True
ผลที่ได้จาก np.histogram2d ลองเอาไปใช้กับ plt.pcolor ก็จะได้ผลเป็นภาพที่เหมือนกับใช้ plt.hist2d แต่แรก
hx,hy = np.meshgrid(hisa[1],hisa[2])
hz = hisa[0].T
plt.figure()
plt.axes(xlim=[-6,6],ylim=[-4,4])
plt.pcolor(hx,hy,hz,cmap='Spectral')
plt.colorbar(pad=0.01)
plt.subplots_adjust(0.05,0.05,1.05,0.95)
plt.show()
นอกจากนี้แล้วก็ยังมีฟังก์ชัน np.histogram สำหรับสร้างค่าเพื่อใช้ทำฮิสโทแกรมหนึ่งมิติ กับ np.histogramdd ที่ใช้เตรียมฮิสโทแกรมกี่มิติก็ได้
อ้างอิง