قم بإنشاء خريطة حرارية في MatPlotLib باستخدام مجموعة بيانات مبعثرة

StackOverflow https://stackoverflow.com/questions/2369492

سؤال

لدي مجموعة من نقاط البيانات 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 heat map example

نصائح أخرى

في 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()   

enter image description here

بدلاً من استخدام 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()

الذي ينتج الصورة التالية:

enter image description here

كما ترى ، تبدو الصور جميلة ، ونحن قادرون على تحديد هياكل أساسية مختلفة عليها. تم بناء هذه الصور تنشر وزنًا معينًا لكل نقطة داخل مجال معين ، محددة بطول تنعيم ، والذي يتم تقديمه بدوره بالمسافة إلى أقرب 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()

gaussian_2d_heat_map

تحرير: للحصول على تقريب أفضل لإجابة أليخاندرو ، انظر أدناه.

أعلم أن هذا سؤال قديم ، لكنني أردت إضافة شيء إلى 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()

ينتج عنه:

Output images

مؤامرة مبعثرة و S = 16 تم رسمها فوق بعضها البعض من أجل agape gal'lo (انقر للحصول على عرض أفضل):

On top of eachother


أحد الاختلافات التي لاحظتها مع نهج المرشح الغوسي ونهج أليخاندرو هو أن طريقته تُظهر الهياكل المحلية أفضل بكثير من لي. لذلك قمت بتنفيذ طريقة أقرب جوار بسيطة على مستوى بكسل. تحسب هذه الطريقة لكل بكسل المبلغ العكسي لمسافات 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()

نتيجة:

Nearest Neighbour Smoothing

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()

demo image

والسؤال الأولي كان..كيفية تحويل القيم المبعثرة إلى قيم الشبكة، أليس كذلك؟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، كما ترى، قمت بإجراء الكثير من الحسابات أثناء بحثي عن النقطة المثالية، في حين أن الأجزاء الأقل إثارة للاهتمام في كل مكان آخر تقريبًا تتمتع بدقة أقل.

Heatmap of a SVC in high resolution

قم بعمل صفيف ثنائي الأبعاد يتوافق مع الخلايا في صورتك النهائية ، تسمى 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()

انتاج:

2d_gaussian_heatmap

أخشى أن أكون متأخراً قليلاً عن الحفلة ، لكن كان لدي سؤال مماثل منذ فترة. ساعدني الإجابة المقبولة (بواسطة 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)


ها هي النتيجةenter image description here

enter image description here

إليك واحدة صنعتها على 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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top