ข้อจำกัดความรับผิดชอบ: ส่วนใหญ่ฉันเขียนโพสต์นี้โดยคำนึงถึงข้อควรพิจารณาด้านไวยากรณ์และพฤติกรรมทั่วไป ฉันไม่คุ้นเคยกับด้านหน่วยความจำและ CPU ของวิธีการที่อธิบายไว้และฉันตั้งเป้าคำตอบนี้ไว้ที่ผู้ที่มีชุดข้อมูลขนาดเล็กพอสมควรดังนั้นคุณภาพของการแก้ไขจึงเป็นประเด็นหลักที่ต้องพิจารณา ฉันทราบดีว่าเมื่อทำงานกับชุดข้อมูลขนาดใหญ่วิธีการที่มีประสิทธิภาพดีกว่า (กล่าวคือgriddata
และRbf
) อาจไม่สามารถทำได้
ฉันจะเปรียบเทียบวิธีการแก้ไขแบบหลายมิติสามแบบ ( interp2d
/ เส้นแบ่งgriddata
และRbf
) ฉันจะกำหนดให้พวกเขาทำงานแก้ไขสองประเภทและฟังก์ชันพื้นฐานสองประเภท (จุดที่จะถูกแก้ไข) ตัวอย่างที่เฉพาะเจาะจงจะแสดงให้เห็นถึงการแก้ไขสองมิติ แต่วิธีการที่เป็นไปได้นั้นสามารถใช้ได้ในมิติที่กำหนดเอง แต่ละวิธีมีการแก้ไขหลายแบบ ในทุกกรณีฉันจะใช้การแก้ไขลูกบาศก์ (หรือสิ่งที่ใกล้เคียง1 ) สิ่งสำคัญคือต้องทราบว่าเมื่อใดก็ตามที่คุณใช้การแก้ไขคุณจะนำอคติไปเปรียบเทียบกับข้อมูลดิบของคุณและวิธีการเฉพาะที่ใช้จะส่งผลต่ออาร์ติแฟกต์ที่คุณจะได้รับ ตระหนักถึงเรื่องนี้เสมอและแก้ไขอย่างมีความรับผิดชอบ
งานแก้ไขสองงานจะเป็น
- การสุ่มตัวอย่าง (ข้อมูลอินพุตอยู่ในตารางสี่เหลี่ยมข้อมูลเอาต์พุตอยู่บนกริดที่หนาแน่นขึ้น)
- การแก้ไขข้อมูลที่กระจัดกระจายไปยังตารางปกติ
ทั้งสองฟังก์ชั่น (บนโดเมน [x,y] in [-1,1]x[-1,1]
) จะเป็น
- ฟังก์ชั่นที่ราบรื่นและเป็นมิตร:
cos(pi*x)*sin(pi*y)
; ช่วงใน[-1, 1]
- ฟังก์ชันชั่วร้าย (และโดยเฉพาะอย่างยิ่งไม่ต่อเนื่อง):
x*y/(x^2+y^2)
มีค่า 0.5 ใกล้จุดกำเนิด ช่วงใน[-0.5, 0.5]
มีลักษณะดังนี้:
ก่อนอื่นฉันจะสาธิตวิธีการทำงานของทั้งสามวิธีภายใต้การทดสอบทั้งสี่นี้จากนั้นฉันจะให้รายละเอียดเกี่ยวกับไวยากรณ์ของทั้งสาม หากคุณรู้ว่าคุณควรคาดหวังอะไรจากวิธีการหนึ่งคุณอาจไม่ต้องการเสียเวลาเรียนรู้ไวยากรณ์ของมัน (มองไปที่คุณinterp2d
)
ข้อมูลการทดสอบ
เพื่อประโยชน์ในการเป็นพยานอย่างชัดเจนนี่คือรหัสที่ฉันสร้างข้อมูลอินพุต แม้ว่าในกรณีเฉพาะนี้ฉันทราบชัดเจนถึงฟังก์ชันที่อยู่ภายใต้ข้อมูล แต่ฉันจะใช้สิ่งนี้เพื่อสร้างอินพุตสำหรับวิธีการแก้ไขเท่านั้น ฉันใช้ numpy เพื่อความสะดวก (และส่วนใหญ่ใช้ในการสร้างข้อมูล) แต่การสแกนเพียงอย่างเดียวก็เพียงพอเช่นกัน
import numpy as np
import scipy.interpolate as interp
# auxiliary function for mesh generation
def gimme_mesh(n):
minval = -1
maxval = 1
# produce an asymmetric shape in order to catch issues with transpositions
return np.meshgrid(np.linspace(minval,maxval,n), np.linspace(minval,maxval,n+1))
# set up underlying test functions, vectorized
def fun_smooth(x, y):
return np.cos(np.pi*x)*np.sin(np.pi*y)
def fun_evil(x, y):
# watch out for singular origin; function has no unique limit there
return np.where(x**2+y**2>1e-10, x*y/(x**2+y**2), 0.5)
# sparse input mesh, 6x7 in shape
N_sparse = 6
x_sparse,y_sparse = gimme_mesh(N_sparse)
z_sparse_smooth = fun_smooth(x_sparse, y_sparse)
z_sparse_evil = fun_evil(x_sparse, y_sparse)
# scattered input points, 10^2 altogether (shape (100,))
N_scattered = 10
x_scattered,y_scattered = np.random.rand(2,N_scattered**2)*2 - 1
z_scattered_smooth = fun_smooth(x_scattered, y_scattered)
z_scattered_evil = fun_evil(x_scattered, y_scattered)
# dense output mesh, 20x21 in shape
N_dense = 20
x_dense,y_dense = gimme_mesh(N_dense)
ฟังก์ชั่นที่ราบรื่นและการสุ่มตัวอย่าง
เริ่มต้นด้วยงานที่ง่ายที่สุด นี่คือวิธีการอัพแซมเพิลจากตาข่ายที่มีรูปร่าง[6,7]
เป็นหนึ่งในวิธีการ[20,21]
ทำงานสำหรับฟังก์ชันการทดสอบที่ราบรื่น:
แม้ว่านี่จะเป็นงานง่ายๆ แต่ก็มีความแตกต่างเล็กน้อยระหว่างเอาต์พุต เมื่อมองแวบแรกเอาต์พุตทั้งสามมีความสมเหตุสมผล มีคุณสมบัติสองประการที่ควรทราบโดยพิจารณาจากความรู้เดิมของเราเกี่ยวกับฟังก์ชันพื้นฐาน: กรณีกลางของgriddata
ข้อมูลที่บิดเบือนมากที่สุด หมายเหตุy==-1
ขอบเขตของพล็อต (ที่ใกล้ที่สุดx
ฉลาก): ฟังก์ชั่นที่ควรจะเป็นอย่างเคร่งครัดศูนย์ (ตั้งแต่y==-1
เป็นสายสำคัญสำหรับการทำงานที่ราบรื่น) griddata
แต่กรณีนี้ไม่ได้สำหรับ สังเกตx==-1
ขอบเขตของพล็อตด้วย (ด้านหลังไปทางซ้าย): ฟังก์ชันพื้นฐานมีค่าสูงสุดในพื้นที่ (หมายถึงการไล่ระดับสีเป็นศูนย์ใกล้กับขอบเขต) ที่[-1, -0.5]
แต่griddata
ผลลัพธ์จะแสดงการไล่ระดับสีที่ไม่ใช่ศูนย์อย่างชัดเจนในภูมิภาคนี้ เอฟเฟกต์นั้นบอบบาง แต่ก็ไม่มีอคติเลยแม้แต่น้อย (ความเที่ยงตรงของRbf
จะดีกว่าด้วยตัวเลือกเริ่มต้นของฟังก์ชั่นเรเดียลพากย์multiquadratic
เสียง)
ฟังก์ชั่นที่ชั่วร้ายและการสุ่มตัวอย่าง
งานที่ยากกว่าเล็กน้อยคือการเพิ่มประสิทธิภาพการทำงานที่ชั่วร้ายของเรา:
ความแตกต่างที่ชัดเจนกำลังเริ่มปรากฏขึ้นในสามวิธี เมื่อมองไปที่แปลงพื้นผิวจะมีเอกซ์เทรมาปลอมที่ชัดเจนปรากฏขึ้นในเอาต์พุตจากinterp2d
(สังเกตสอง humps ทางด้านขวาของพื้นผิวที่ลงจุด) ในขณะที่griddata
และRbf
ดูเหมือนว่าจะให้ผลลัพธ์ที่คล้ายกันในตอนแรก แต่อย่างหลังดูเหมือนว่าจะให้ผลลัพธ์ขั้นต่ำที่ลึกกว่าซึ่งอยู่ใกล้กับ[0.4, -0.4]
ฟังก์ชันพื้นฐาน
อย่างไรก็ตามมีลักษณะสำคัญอย่างหนึ่งที่Rbf
เหนือกว่ามากนั่นคือเคารพความสมมาตรของฟังก์ชันพื้นฐาน (ซึ่งแน่นอนว่าเป็นไปได้ด้วยความสมมาตรของตาข่ายตัวอย่าง) ผลลัพธ์จากการgriddata
แบ่งสมมาตรของจุดตัวอย่างซึ่งมองเห็นได้ไม่ชัดเจนในกรณีที่เรียบ
ฟังก์ชั่นที่ราบรื่นและข้อมูลที่กระจัดกระจาย
ส่วนใหญ่มักจะต้องการแก้ไขข้อมูลที่กระจัดกระจาย ด้วยเหตุนี้ฉันจึงคาดว่าการทดสอบเหล่านี้จะมีความสำคัญมากขึ้น ดังที่แสดงไว้ด้านบนจุดตัวอย่างถูกเลือกหลอกอย่างสม่ำเสมอในโดเมนที่สนใจ ในสถานการณ์จริงคุณอาจมีเสียงรบกวนเพิ่มเติมในการวัดแต่ละครั้งและคุณควรพิจารณาว่าเหมาะสมหรือไม่ที่จะแก้ไขข้อมูลดิบของคุณเพื่อเริ่มต้น
เอาต์พุตสำหรับฟังก์ชันที่ราบรื่น:
ตอนนี้มีการแสดงสยองขวัญเกิดขึ้นแล้ว ฉันตัดผลลัพธ์จากinterp2d
ไปยังระหว่าง[-1, 1]
การวางแผนโดยเฉพาะเพื่อรักษาข้อมูลอย่างน้อยที่สุด เป็นที่ชัดเจนว่าในขณะที่มีรูปร่างพื้นฐานบางส่วนมีบริเวณที่มีเสียงดังขนาดใหญ่ซึ่งวิธีการนี้พังลงอย่างสมบูรณ์ กรณีที่สองของgriddata
การทำซ้ำรูปร่างค่อนข้างสวยงาม แต่สังเกตบริเวณสีขาวที่ขอบของโครงร่าง นี่เป็นเพราะการgriddata
ทำงานภายในตัวถังนูนของจุดข้อมูลอินพุตเท่านั้น (กล่าวอีกนัยหนึ่งก็คือจะไม่ทำการคาดคะเนใด ๆ) ฉันเก็บค่า NaN เริ่มต้นสำหรับจุดเอาต์พุตที่อยู่นอกตัวถังแบบนูน 2เมื่อพิจารณาถึงคุณสมบัติเหล่านี้Rbf
ดูเหมือนจะทำงานได้ดีที่สุด
ทำหน้าที่ชั่วร้ายและข้อมูลที่กระจัดกระจาย
และช่วงเวลาที่เรารอคอย:
ไม่แปลกใจเลยที่interp2d
ยอมแพ้ ในความเป็นจริงในระหว่างการโทรหาinterp2d
คุณควรคาดหวังว่าผู้ที่เป็นมิตรRuntimeWarning
จะบ่นเกี่ยวกับความเป็นไปไม่ได้ที่จะสร้างเส้นโค้ง สำหรับอีกสองวิธีRbf
ดูเหมือนว่าจะให้ผลลัพธ์ที่ดีที่สุดแม้ว่าจะอยู่ใกล้กับขอบเขตของโดเมนซึ่งผลลัพธ์จะถูกคาดการณ์
ขอฉันพูดสองสามคำเกี่ยวกับสามวิธีโดยเรียงลำดับความต้องการที่ลดลง (เพื่อให้สิ่งที่เลวร้ายที่สุดคือโอกาสที่ใครจะอ่านน้อยที่สุด)
scipy.interpolate.Rbf
Rbf
ระดับย่อมาจาก "ฟังก์ชั่นพื้นฐานรัศมี" พูดตามตรงฉันไม่เคยพิจารณาแนวทางนี้เลยจนกระทั่งฉันเริ่มค้นคว้าเกี่ยวกับโพสต์นี้ แต่ฉันค่อนข้างมั่นใจว่าฉันจะใช้สิ่งเหล่านี้ในอนาคต
เช่นเดียวกับเมธอดที่ใช้ spline (ดูในภายหลัง) การใช้งานมีสองขั้นตอน: ขั้นแรกสร้างRbf
อินสแตนซ์คลาสที่เรียกได้ตามข้อมูลอินพุตจากนั้นเรียกอ็อบเจ็กต์นี้สำหรับเอาท์พุตที่กำหนดเพื่อให้ได้ผลลัพธ์ที่ถูกแก้ไข ตัวอย่างจากการทดสอบการสุ่มตัวอย่างแบบเรียบ:
import scipy.interpolate as interp
zfun_smooth_rbf = interp.Rbf(x_sparse, y_sparse, z_sparse_smooth, function='cubic', smooth=0) # default smooth=0 for interpolation
z_dense_smooth_rbf = zfun_smooth_rbf(x_dense, y_dense) # not really a function, but a callable class instance
โปรดทราบว่าทั้งอินพุตและเอาต์พุตเป็นอาร์เรย์ 2d ในกรณีนี้และเอาต์พุตz_dense_smooth_rbf
มีรูปร่างเหมือนกันx_dense
และy_dense
ไม่ต้องใช้ความพยายามใด ๆ โปรดทราบว่าRbf
รองรับขนาดที่กำหนดเองสำหรับการแก้ไข
ดังนั้น, scipy.interpolate.Rbf
- สร้างเอาต์พุตที่มีพฤติกรรมดีแม้สำหรับข้อมูลอินพุตที่บ้าคลั่ง
- รองรับการแก้ไขในมิติข้อมูลที่สูงขึ้น
- การคาดการณ์ภายนอกตัวถังนูนของจุดอินพุต (แน่นอนว่าการคาดการณ์เป็นการพนันเสมอและโดยทั่วไปคุณไม่ควรพึ่งพามันเลย)
- สร้าง interpolator เป็นขั้นตอนแรกดังนั้นการประเมินในจุดเอาต์พุตต่างๆจึงไม่ต้องใช้ความพยายามเพิ่มเติม
- สามารถมีจุดเอาต์พุตที่มีรูปร่างตามอำเภอใจ (ตรงข้ามกับการถูก จำกัด ไว้ที่ตาข่ายสี่เหลี่ยมดูภายหลัง)
- มีแนวโน้มที่จะรักษาความสมมาตรของข้อมูลอินพุต
- รองรับหลายชนิดของฟังก์ชั่นสำหรับคำหลักรัศมี
function
: multiquadric
, inverse
, gaussian
, linear
, cubic
, quintic
, thin_plate
และผู้ใช้กำหนดเองโดยพลการ
scipy.interpolate.griddata
อดีตที่ฉันชอบgriddata
คือม้าทำงานทั่วไปสำหรับการแก้ไขในมิติที่กำหนดเอง มันไม่ได้ทำการคาดคะเนเกินกว่าการกำหนดค่าที่ตั้งไว้ล่วงหน้าเพียงจุดเดียวสำหรับจุดที่อยู่นอกตัวถังนูนของจุดสำคัญ แต่เนื่องจากการประมาณค่าเป็นสิ่งที่ไม่แน่นอนและเป็นอันตรายมากจึงไม่จำเป็นต้องเป็นข้อเสีย ตัวอย่างการใช้งาน:
z_dense_smooth_griddata = interp.griddata(np.array([x_sparse.ravel(),y_sparse.ravel()]).T,
z_sparse_smooth.ravel(),
(x_dense,y_dense), method='cubic') # default method is linear
สังเกตไวยากรณ์ kludgy เล็กน้อย ต้องระบุจุดอินพุตในอาร์เรย์ของรูปร่าง[N, D]
ในD
มิติ สำหรับสิ่งนี้ก่อนอื่นเราต้องทำให้อาร์เรย์พิกัด 2d ของเราแบน (โดยใช้ravel
) จากนั้นเชื่อมอาร์เรย์เข้าด้วยกันและเปลี่ยนผลลัพธ์ มีหลายวิธีในการทำเช่นนี้ แต่ทั้งหมดดูเหมือนจะใหญ่โต ข้อมูลที่ป้อนz
จะต้องถูกแบนด้วย เรามีอิสระมากขึ้นเล็กน้อยเมื่อพูดถึงจุดเอาท์พุต: ด้วยเหตุผลบางประการสิ่งเหล่านี้สามารถระบุเป็นทูเพิลของอาร์เรย์หลายมิติได้ โปรดทราบว่าhelp
ของgriddata
ทำให้เข้าใจผิดเนื่องจากชี้ให้เห็นว่าสิ่งเดียวกันนี้เป็นจริงสำหรับจุดอินพุต (อย่างน้อยสำหรับเวอร์ชัน 0.17.0):
griddata(points, values, xi, method='linear', fill_value=nan, rescale=False)
Interpolate unstructured D-dimensional data.
Parameters
----------
points : ndarray of floats, shape (n, D)
Data point coordinates. Can either be an array of
shape (n, D), or a tuple of `ndim` arrays.
values : ndarray of float or complex, shape (n,)
Data values.
xi : ndarray of float, shape (M, D)
Points at which to interpolate data.
โดยสังเขป, scipy.interpolate.griddata
- สร้างเอาต์พุตที่มีพฤติกรรมดีแม้สำหรับข้อมูลอินพุตที่บ้าคลั่ง
- รองรับการแก้ไขในมิติข้อมูลที่สูงขึ้น
- ไม่ทำการคาดคะเนสามารถกำหนดค่าเดียวสำหรับเอาต์พุตภายนอกตัวถังนูนของจุดอินพุตได้ (ดู
fill_value
)
- คำนวณค่าที่ถูกแก้ไขในการเรียกครั้งเดียวดังนั้นการตรวจสอบจุดเอาต์พุตหลายชุดจึงเริ่มต้นจากศูนย์
- สามารถมีจุดเอาต์พุตของรูปร่างโดยพลการ
- รองรับเพื่อนบ้านที่ใกล้ที่สุดและการแก้ไขเชิงเส้นในขนาดโดยพลการลูกบาศก์ใน 1d และ 2d เพื่อนบ้านที่ใกล้ที่สุดและการใช้การแก้ไขเชิงเส้น
NearestNDInterpolator
และLinearNDInterpolator
ใต้ฝากระโปรงตามลำดับ การแก้ไขลูกบาศก์ 1d ใช้ spline การแก้ไข 2d ลูกบาศก์ใช้CloughTocher2DInterpolator
ในการสร้างตัวปรับค่าขนาดลูกบาศก์ - ลูกบาศก์
- อาจละเมิดความสมมาตรของข้อมูลอินพุต
scipy.interpolate.interp2d
/scipy.interpolate.bisplrep
เหตุผลเดียวที่ฉันพูดคุยinterp2d
และญาติ ๆ ก็คือชื่อนี้มีชื่อหลอกลวงและผู้คนมักจะลองใช้มัน การแจ้งเตือนสปอยเลอร์: อย่าใช้มัน (ในเวอร์ชัน scipy 0.17.0) มันพิเศษกว่าวิชาก่อนหน้านี้อยู่แล้วตรงที่ใช้สำหรับการแก้ไขสองมิติโดยเฉพาะ แต่ฉันสงสัยว่านี่เป็นกรณีที่พบบ่อยที่สุดสำหรับการแก้ไขหลายตัวแปร
ตราบใดที่ไวยากรณ์ดำเนินไปinterp2d
ก็คล้ายกับRbf
ที่ต้องสร้างอินสแตนซ์การแก้ไขก่อนซึ่งสามารถเรียกได้ว่าให้ค่าที่ถูกแก้ไขตามความเป็นจริง อย่างไรก็ตามมีจุดจับ: จุดเอาต์พุตจะต้องอยู่บนตาข่ายสี่เหลี่ยมดังนั้นอินพุตที่จะเข้าสู่การเรียกไปยังอินเทอร์โพเลเตอร์จะต้องเป็นเวกเตอร์ 1d ซึ่งครอบคลุมกริดเอาต์พุตราวกับว่ามาจากnumpy.meshgrid
:
# reminder: x_sparse and y_sparse are of shape [6, 7] from numpy.meshgrid
zfun_smooth_interp2d = interp.interp2d(x_sparse, y_sparse, z_sparse_smooth, kind='cubic') # default kind is 'linear'
# reminder: x_dense and y_dense are of shape [20, 21] from numpy.meshgrid
xvec = x_dense[0,:] # 1d array of unique x values, 20 elements
yvec = y_dense[:,0] # 1d array of unique y values, 21 elements
z_dense_smooth_interp2d = zfun_smooth_interp2d(xvec,yvec) # output is [20, 21]-shaped array
หนึ่งในที่สุดข้อผิดพลาดทั่วไปเมื่อใช้interp2d
มีการวางตาข่าย 2d MemoryError
เต็มของคุณลงในสายการแก้ไขซึ่งนำไปสู่การใช้หน่วยความจำระเบิดและหวังว่าจะรีบร้อน
ตอนนี้ปัญหาที่ใหญ่ที่สุดinterp2d
คือมักใช้ไม่ได้ เพื่อให้เข้าใจสิ่งนี้เราต้องมองไปที่ใต้ฝากระโปรง ปรากฎว่าinterp2d
เป็นกระดาษห่อหุ้มสำหรับฟังก์ชั่นระดับล่างbisplrep
+ bisplev
ซึ่งอยู่ในทางกลับกันห่อสำหรับกิจวัตร FITPACK (เขียนด้วยภาษา Fortran) การเรียกที่เทียบเท่ากับตัวอย่างก่อนหน้านี้จะเป็น
kind = 'cubic'
if kind=='linear':
kx=ky=1
elif kind=='cubic':
kx=ky=3
elif kind=='quintic':
kx=ky=5
# bisplrep constructs a spline representation, bisplev evaluates the spline at given points
bisp_smooth = interp.bisplrep(x_sparse.ravel(),y_sparse.ravel(),z_sparse_smooth.ravel(),kx=kx,ky=ky,s=0)
z_dense_smooth_bisplrep = interp.bisplev(xvec,yvec,bisp_smooth).T # note the transpose
ตอนนี้นี่คือสิ่งที่เกี่ยวกับinterp2d
: (ในเวอร์ชัน scipy 0.17.0) มีความคิดเห็นที่interpolate/interpolate.py
ดีสำหรับinterp2d
:
if not rectangular_grid:
# TODO: surfit is really not meant for interpolation!
self.tck = fitpack.bisplrep(x, y, z, kx=kx, ky=ky, s=0.0)
และในinterpolate/fitpack.py
นั้นbisplrep
มีการตั้งค่าบางอย่างและท้ายที่สุด
tx, ty, c, o = _fitpack._surfit(x, y, z, w, xb, xe, yb, ye, kx, ky,
task, s, eps, tx, ty, nxest, nyest,
wrk, lwrk1, lwrk2)
และนั่นแหล่ะ กิจวัตรพื้นฐานinterp2d
ไม่ได้หมายถึงการแก้ไขอย่างแท้จริง อาจเพียงพอสำหรับข้อมูลที่มีพฤติกรรมดีเพียงพอ แต่ภายใต้สถานการณ์จริงคุณอาจต้องการใช้อย่างอื่น
เพื่อสรุป interpolate.interp2d
- สามารถนำไปสู่อาร์ติแฟกต์ได้แม้จะมีข้อมูลที่มีอารมณ์ดีก็ตาม
- มีไว้สำหรับปัญหา bivariate โดยเฉพาะ (แม้ว่าจะมีข้อ จำกัด
interpn
สำหรับจุดอินพุตที่กำหนดบนกริดก็ตาม)
- ทำการคาดคะเน
- สร้าง interpolator เป็นขั้นตอนแรกดังนั้นการประเมินในจุดเอาต์พุตต่างๆจึงไม่ต้องใช้ความพยายามเพิ่มเติม
- สามารถสร้างเอาต์พุตบนตารางสี่เหลี่ยมเท่านั้นสำหรับเอาต์พุตที่กระจัดกระจายคุณจะต้องเรียก interpolator ในลูป
- รองรับการแก้ไขเชิงเส้นลูกบาศก์และควินติก
- อาจละเมิดความสมมาตรของข้อมูลอินพุต
1ฉันค่อนข้างมั่นใจว่าฟังก์ชันพื้นฐานcubic
และlinear
ชนิดของฟังก์ชันพื้นฐานRbf
ไม่ตรงกับตัวแปลอื่น ๆ ที่มีชื่อเดียวกัน
2 NaN เหล่านี้ยังเป็นสาเหตุที่ทำให้พล็อตพื้นผิวดูแปลก ๆ : ในอดีต matplotlib มีปัญหาในการพล็อตวัตถุ 3 มิติที่ซับซ้อนด้วยข้อมูลเชิงลึกที่เหมาะสม ค่า NaN ในข้อมูลทำให้ตัวแสดงผลสับสนดังนั้นบางส่วนของพื้นผิวที่ควรอยู่ด้านหลังจึงถูกพล็อตให้อยู่ด้านหน้า นี่เป็นปัญหาเกี่ยวกับการแสดงภาพและไม่ใช่การแก้ไข