قم بإنشاء خريطة حرارية في MatPlotLib باستخدام مجموعة بيانات مبعثرة
-
24-09-2019 - |
سؤال
لدي مجموعة من نقاط البيانات X وY (حوالي 10 كيلو بايت) التي يسهل رسمها كمخطط مبعثر ولكنني أرغب في تمثيلها كخريطة حرارية.
لقد بحثت في الأمثلة في MatPlotLib ويبدو أنها جميعًا تبدأ بالفعل بقيم خلايا الخريطة الحرارية لإنشاء الصورة.
هل هناك طريقة لتحويل مجموعة من x، y، كلها مختلفة، إلى خريطة حرارية (حيث تكون المناطق ذات التردد الأعلى لـ x، y "أكثر دفئًا")؟
المحلول
إذا كنت لا تريد السداسي ، فيمكنك استخدام Numpy's histogram2d
وظيفة:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# Generate some test data
x = np.random.randn(8873)
y = np.random.randn(8873)
heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()
هذا يجعل خريطة الحرارة 50x50. إذا كنت تريد ، على سبيل المثال ، 512 × 384 ، يمكنك وضع bins=(512, 384)
في الدعوة إلى histogram2d
.
مثال:
نصائح أخرى
في Matplotlib معجم ، أعتقد أنك تريد أ hexbin حبكة.
إذا لم تكن على دراية بهذا النوع من المؤامرة ، فهو مجرد ملف الرسم البياني ثنائي المتغير حيث يتم التغلب على الطائرة XY بواسطة شبكة منتظمة من السداسي.
لذلك من رسم بياني ، يمكنك فقط حساب عدد النقاط التي تقع في كل سداسي ، وتقدير منطقة التخطيط كمجموعة من شبابيك, ، قم بتعيين كل نقطة إلى أحد هذه النوافذ ؛ أخيرًا ، قم بتخطيط النوافذ على أ صفيف اللون, ، ولديك مخطط سداسي.
على الرغم من الاستخدام الأقل استخدامًا من EG ، فإن الدوائر أو المربعات ، بأن السداسي هو خيار أفضل لهندسة حاوية binning بديهي:
السداسي أقرباء الجوار (على سبيل المثال ، لا تفعل الصناديق المربعة ، على سبيل المثال ، المسافة من نقطة على حدود المربع إلى نقطة داخل هذا المربع ليست متساوية في كل مكان) و
Hexagon هو أعلى n-polygon الذي يعطي تفسر الطائرة العادية (أي ، يمكنك إعادة تشكيل أرضية مطبخك بأمان مع بلاط سداسي على شكل سداسي لأنك لن يكون لديك أي مساحة فراغ بين البلاط عند الانتهاء-وليس صحيحًا بالنسبة لجميع الآخرين ، n> = 7 ، المضلعات ).
(Matplotlib يستخدم المصطلح hexbin حبكة؛ وكذلك (AFAIK) كل من تخطيط المكتبات إلى عن على ص; ؛ ما زلت لا أعرف ما إذا كان هذا هو المصطلح المقبول عمومًا للمؤامرات من هذا النوع ، على الرغم من أنني أظن أنه من المحتمل أن يعطى ذلك hexbin هو اختصار ل سداسية binning, ، الذي يصف الخطوة الأساسية في إعداد البيانات للعرض.)
from matplotlib import pyplot as PLT
from matplotlib import cm as CM
from matplotlib import mlab as ML
import numpy as NP
n = 1e5
x = y = NP.linspace(-5, 5, 100)
X, Y = NP.meshgrid(x, y)
Z1 = ML.bivariate_normal(X, Y, 2, 2, 0, 0)
Z2 = ML.bivariate_normal(X, Y, 4, 1, 1, 1)
ZD = Z2 - Z1
x = X.ravel()
y = Y.ravel()
z = ZD.ravel()
gridsize=30
PLT.subplot(111)
# if 'bins=None', then color of each hexagon corresponds directly to its count
# 'C' is optional--it maps values to x-y coordinates; if 'C' is None (default) then
# the result is a pure 2D histogram
PLT.hexbin(x, y, C=z, gridsize=gridsize, cmap=CM.jet, bins=None)
PLT.axis([x.min(), x.max(), y.min(), y.max()])
cb = PLT.colorbar()
cb.set_label('mean value')
PLT.show()
بدلاً من استخدام np.hist2d ، والذي ينتج بشكل عام رسم بياني قبيح للغاية ، أود إعادة التدوير py-sphviewer, ، حزمة Python لتقديم محاكاة الجسيمات باستخدام نواة تجانس تكيفية ويمكن تثبيتها بسهولة من PIP (انظر وثائق صفحة الويب). النظر في الكود التالي ، والذي يعتمد على المثال:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
import sphviewer as sph
def myplot(x, y, nb=32, xsize=500, ysize=500):
xmin = np.min(x)
xmax = np.max(x)
ymin = np.min(y)
ymax = np.max(y)
x0 = (xmin+xmax)/2.
y0 = (ymin+ymax)/2.
pos = np.zeros([3, len(x)])
pos[0,:] = x
pos[1,:] = y
w = np.ones(len(x))
P = sph.Particles(pos, w, nb=nb)
S = sph.Scene(P)
S.update_camera(r='infinity', x=x0, y=y0, z=0,
xsize=xsize, ysize=ysize)
R = sph.Render(S)
R.set_logscale()
img = R.get_image()
extent = R.get_extent()
for i, j in zip(xrange(4), [x0,x0,y0,y0]):
extent[i] += j
print extent
return img, extent
fig = plt.figure(1, figsize=(10,10))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)
# Generate some test data
x = np.random.randn(1000)
y = np.random.randn(1000)
#Plotting a regular scatter plot
ax1.plot(x,y,'k.', markersize=5)
ax1.set_xlim(-3,3)
ax1.set_ylim(-3,3)
heatmap_16, extent_16 = myplot(x,y, nb=16)
heatmap_32, extent_32 = myplot(x,y, nb=32)
heatmap_64, extent_64 = myplot(x,y, nb=64)
ax2.imshow(heatmap_16, extent=extent_16, origin='lower', aspect='auto')
ax2.set_title("Smoothing over 16 neighbors")
ax3.imshow(heatmap_32, extent=extent_32, origin='lower', aspect='auto')
ax3.set_title("Smoothing over 32 neighbors")
#Make the heatmap using a smoothing over 64 neighbors
ax4.imshow(heatmap_64, extent=extent_64, origin='lower', aspect='auto')
ax4.set_title("Smoothing over 64 neighbors")
plt.show()
الذي ينتج الصورة التالية:
كما ترى ، تبدو الصور جميلة ، ونحن قادرون على تحديد هياكل أساسية مختلفة عليها. تم بناء هذه الصور تنشر وزنًا معينًا لكل نقطة داخل مجال معين ، محددة بطول تنعيم ، والذي يتم تقديمه بدوره بالمسافة إلى أقرب NB جار (لقد اخترت 16 و 32 و 64 للأمثلة). لذلك ، تنتشر مناطق الكثافة الأعلى عادةً على مناطق أصغر مقارنةً بمناطق الكثافة المنخفضة.
الوظيفة MyPlot هي مجرد وظيفة بسيطة جدًا كتبتها من أجل إعطاء بيانات X ، Y إلى Py-Sphviewer للقيام بالسحر.
إذا كنت تستخدم 1.2.x
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(100000)
y = np.random.randn(100000)
plt.hist2d(x,y,bins=100)
plt.show()
تحرير: للحصول على تقريب أفضل لإجابة أليخاندرو ، انظر أدناه.
أعلم أن هذا سؤال قديم ، لكنني أردت إضافة شيء إلى Anwser من Alejandro: إذا كنت تريد صورة ناعمة لطيفة دون استخدام py-sphiewer ، يمكنك الاستخدام بدلاً من ذلك np.histogram2d
وتطبيق مرشح غاوسي (من scipy.ndimage.filters
) إلى خريطة الحرارة:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy.ndimage.filters import gaussian_filter
def myplot(x, y, s, bins=1000):
heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins)
heatmap = gaussian_filter(heatmap, sigma=s)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
return heatmap.T, extent
fig, axs = plt.subplots(2, 2)
# Generate some test data
x = np.random.randn(1000)
y = np.random.randn(1000)
sigmas = [0, 16, 32, 64]
for ax, s in zip(axs.flatten(), sigmas):
if s == 0:
ax.plot(x, y, 'k.', markersize=5)
ax.set_title("Scatter plot")
else:
img, extent = myplot(x, y, s)
ax.imshow(img, extent=extent, origin='lower', cmap=cm.jet)
ax.set_title("Smoothing with $\sigma$ = %d" % s)
plt.show()
ينتج عنه:
مؤامرة مبعثرة و S = 16 تم رسمها فوق بعضها البعض من أجل agape gal'lo (انقر للحصول على عرض أفضل):
أحد الاختلافات التي لاحظتها مع نهج المرشح الغوسي ونهج أليخاندرو هو أن طريقته تُظهر الهياكل المحلية أفضل بكثير من لي. لذلك قمت بتنفيذ طريقة أقرب جوار بسيطة على مستوى بكسل. تحسب هذه الطريقة لكل بكسل المبلغ العكسي لمسافات n
أقرب نقاط في البيانات. هذه الطريقة باهظة الثمن للغاية من الناحية الحسابية وأعتقد أن هناك طريقة أسرع ، لذلك اسمحوا لي أن أعرف إذا كان لديك أي تحسينات. على أي حال ، إليك الرمز:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
def data_coord2view_coord(p, vlen, pmin, pmax):
dp = pmax - pmin
dv = (p - pmin) / dp * vlen
return dv
def nearest_neighbours(xs, ys, reso, n_neighbours):
im = np.zeros([reso, reso])
extent = [np.min(xs), np.max(xs), np.min(ys), np.max(ys)]
xv = data_coord2view_coord(xs, reso, extent[0], extent[1])
yv = data_coord2view_coord(ys, reso, extent[2], extent[3])
for x in range(reso):
for y in range(reso):
xp = (xv - x)
yp = (yv - y)
d = np.sqrt(xp**2 + yp**2)
im[y][x] = 1 / np.sum(d[np.argpartition(d.ravel(), n_neighbours)[:n_neighbours]])
return im, extent
n = 1000
xs = np.random.randn(n)
ys = np.random.randn(n)
resolution = 250
fig, axes = plt.subplots(2, 2)
for ax, neighbours in zip(axes.flatten(), [0, 16, 32, 64]):
if neighbours == 0:
ax.plot(xs, ys, 'k.', markersize=2)
ax.set_aspect('equal')
ax.set_title("Scatter Plot")
else:
im, extent = nearest_neighbours(xs, ys, resolution, neighbours)
ax.imshow(im, origin='lower', extent=extent, cmap=cm.jet)
ax.set_title("Smoothing over %d neighbours" % neighbours)
ax.set_xlim(extent[0], extent[1])
ax.set_ylim(extent[2], extent[3])
plt.show()
نتيجة:
Seaborn لديه الآن وظيفة pointplot التي يجب أن تعمل بشكل جيد هنا:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# Generate some test data
x = np.random.randn(8873)
y = np.random.randn(8873)
sns.jointplot(x=x, y=y, kind='hex')
plt.show()
والسؤال الأولي كان..كيفية تحويل القيم المبعثرة إلى قيم الشبكة، أليس كذلك؟histogram2d
يقوم بحساب التكرار لكل خلية، ومع ذلك، إذا كان لديك بيانات أخرى لكل خلية غير التردد فقط، فستحتاج إلى بعض العمل الإضافي للقيام به.
x = data_x # between -10 and 4, log-gamma of an svc
y = data_y # between -4 and 11, log-C of an svc
z = data_z #between 0 and 0.78, f1-values from a difficult dataset
لذا، لدي مجموعة بيانات تحتوي على نتائج Z لإحداثيات X وY.ومع ذلك، كنت أحسب بضع نقاط خارج منطقة الاهتمام (فجوات كبيرة)، وأكوام من النقاط في منطقة صغيرة من الاهتمام.
نعم هنا يصبح الأمر أكثر صعوبة ولكنه أيضًا أكثر متعة.بعض المكتبات (آسف):
from matplotlib import pyplot as plt
from matplotlib import cm
import numpy as np
from scipy.interpolate import griddata
Pyplot هو محرك الرسوم الخاص بي اليوم ، CM عبارة عن مجموعة من خرائط الألوان مع بعض الخيارات المبدئية.numpy للحسابات ، و Griddata لتوصيل القيم بشبكة ثابتة.
الأخير مهم خاصة لأن تكرار نقاط xy لا يتم توزيعه بالتساوي في بياناتي.أولاً، لنبدأ ببعض الحدود الملائمة لبياناتي وحجم الشبكة العشوائي.تحتوي البيانات الأصلية على نقاط بيانات أيضًا خارج حدود x وy.
#determine grid boundaries
gridsize = 500
x_min = -8
x_max = 2.5
y_min = -2
y_max = 7
لذلك قمنا بتعريف شبكة تحتوي على 500 بكسل بين القيمتين الدنيا والقصوى لـ x وy.
في بياناتي، هناك أكثر من 500 قيمة متاحة في المنطقة ذات الاهتمام العالي؛بينما في منطقة الفائدة المنخفضة، لا توجد حتى 200 قيمة في الشبكة الإجمالية؛بين الحدود الرسومية x_min
و x_max
هناك أقل من ذلك.
لذا، للحصول على صورة جميلة، فإن المهمة هي الحصول على متوسط لقيم الفائدة المرتفعة وملء الفجوات في أماكن أخرى.
أقوم بتحديد شبكتي الآن.لكل زوج من xx-yy، أريد الحصول على لون.
xx = np.linspace(x_min, x_max, gridsize) # array of x values
yy = np.linspace(y_min, y_max, gridsize) # array of y values
grid = np.array(np.meshgrid(xx, yy.T))
grid = grid.reshape(2, grid.shape[1]*grid.shape[2]).T
لماذا الشكل الغريب؟ scipy.griddata يريد شكل (ن، د).
تقوم Griddata بحساب قيمة واحدة لكل نقطة في الشبكة، بطريقة محددة مسبقًا.اخترت "الأقرب" - سيتم ملء نقاط الشبكة الفارغة بقيم من أقرب جار.يبدو هذا كما لو أن المناطق ذات المعلومات الأقل تحتوي على خلايا أكبر (حتى لو لم يكن الأمر كذلك).يمكن للمرء أن يختار الاستيفاء "الخطي"، ثم تبدو المناطق ذات المعلومات الأقل أقل وضوحًا.مسألة ذوق، حقا.
points = np.array([x, y]).T # because griddata wants it that way
z_grid2 = griddata(points, z, grid, method='nearest')
# you get a 1D vector as result. Reshape to picture format!
z_grid2 = z_grid2.reshape(xx.shape[0], yy.shape[0])
ونقفز، نسلم إلى matplotlib لعرض المؤامرة
fig = plt.figure(1, figsize=(10, 10))
ax1 = fig.add_subplot(111)
ax1.imshow(z_grid2, extent=[x_min, x_max,y_min, y_max, ],
origin='lower', cmap=cm.magma)
ax1.set_title("SVC: empty spots filled by nearest neighbours")
ax1.set_xlabel('log gamma')
ax1.set_ylabel('log C')
plt.show()
حول الجزء المدبب من الشكل V، كما ترى، قمت بإجراء الكثير من الحسابات أثناء بحثي عن النقطة المثالية، في حين أن الأجزاء الأقل إثارة للاهتمام في كل مكان آخر تقريبًا تتمتع بدقة أقل.
قم بعمل صفيف ثنائي الأبعاد يتوافق مع الخلايا في صورتك النهائية ، تسمى Say heatmap_cells
وتثبيتها كجميع الأصفار.
اختر عاملين تحجيمين يحددان الفرق بين كل عنصر صفيف في الوحدات الحقيقية ، لكل بعد ، على سبيل المثال x_scale
و y_scale
. اختر هذه بحيث تقع جميع نقاط البيانات الخاصة بك داخل حدود صفيف خريطة الحرارة.
لكل نقطة بيانات خام مع x_value
و y_value
:
heatmap_cells[floor(x_value/x_scale),floor(y_value/y_scale)]+=1
تشبه الى حد بعيد @إجابة بيتي, ، ولكن باستخدام مكالمة واحدة بدلاً من 2 لإنشاء النقاط:
import numpy as np
import matplotlib.pyplot as plt
pts = 1000000
mean = [0.0, 0.0]
cov = [[1.0,0.0],[0.0,1.0]]
x,y = np.random.multivariate_normal(mean, cov, pts).T
plt.hist2d(x, y, bins=50, cmap=plt.cm.jet)
plt.show()
انتاج:
أخشى أن أكون متأخراً قليلاً عن الحفلة ، لكن كان لدي سؤال مماثل منذ فترة. ساعدني الإجابة المقبولة (بواسطة ptomato) ، لكنني أرغب أيضًا في نشر هذا في حالة استخدامها لشخص ما.
''' I wanted to create a heatmap resembling a football pitch which would show the different actions performed '''
import numpy as np
import matplotlib.pyplot as plt
import random
#fixing random state for reproducibility
np.random.seed(1234324)
fig = plt.figure(12)
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
#Ratio of the pitch with respect to UEFA standards
hmap= np.full((6, 10), 0)
#print(hmap)
xlist = np.random.uniform(low=0.0, high=100.0, size=(20))
ylist = np.random.uniform(low=0.0, high =100.0, size =(20))
#UEFA Pitch Standards are 105m x 68m
xlist = (xlist/100)*10.5
ylist = (ylist/100)*6.5
ax1.scatter(xlist,ylist)
#int of the co-ordinates to populate the array
xlist_int = xlist.astype (int)
ylist_int = ylist.astype (int)
#print(xlist_int, ylist_int)
for i, j in zip(xlist_int, ylist_int):
#this populates the array according to the x,y co-ordinate values it encounters
hmap[j][i]= hmap[j][i] + 1
#Reversing the rows is necessary
hmap = hmap[::-1]
#print(hmap)
im = ax2.imshow(hmap)
إليك واحدة صنعتها على 1 مليون نقطة مع 3 فئات (اللون الأحمر والأخضر والأزرق). إليك رابط للمستودع إذا كنت ترغب في تجربة الوظيفة. جيثب ريبو
histplot(
X,
Y,
labels,
bins=2000,
range=((-3,3),(-3,3)),
normalize_each_label=True,
colors = [
[1,0,0],
[0,1,0],
[0,0,1]],
gain=50)