Rozmiar heksa w heksinach matplotlib na podstawie gęstości pobliskich punktów

9

Mam następujący kod, który tworzy następujący rysunek

import numpy as np
np.random.seed(3)
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame()
df['X'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))
df['Y'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))

df['Bin'] = df.apply(lambda row: .1 if row['X'] < 30 and row['Y'] < 30 else .9, axis=1)

fig, ax = plt.subplots(figsize=(10,10))
plt.scatter(df['X'], df['Y'])

rozpraszać

Zarysowałem dane za pomocą heksbins, jak zauważono poniżej

from matplotlib import cm

fig, ax = plt.subplots(figsize=(10,10))
hexbin = ax.hexbin(df['X'], df['Y'], C=df['Bin'], gridsize=20, cmap= cm.get_cmap('RdYlBu_r'),edgecolors='black')
plt.show()

sześciokąty

Chciałbym zmienić rozmiar sześciokątów w oparciu o gęstość punktów wykreślonych w obszarze pokrywanym przez sześciokąt. Na przykład sześciokąty w lewym dolnym rogu (gdzie punkty są zwarte) będą większe niż sześciokąty wszędzie indziej (gdzie punkty są rzadkie). Czy jest na to sposób?

Edycja: Wypróbowałem to rozwiązanie , ale nie mogę wymyślić, jak pokolorować heksy na podstawie df [„Bin”], ani jak ustawić minimalny i maksymalny rozmiar heksa.

from matplotlib.collections import PatchCollection
from matplotlib.path import Path
from matplotlib.patches import PathPatch
fig, ax = plt.subplots(figsize=(10,10))
hexbin = ax.hexbin(df['X'], df['Y'], C=df['Bins'], gridsize=20, cmap= cm.get_cmap('RdYlBu_r'),edgecolors='black')
def sized_hexbin(ax,hc):
    offsets = hc.get_offsets()
    orgpath = hc.get_paths()[0]
    verts = orgpath.vertices
    values = hc.get_array()
    ma = values.max()
    patches = []
    for offset,val in zip(offsets,values):
        v1 = verts*val/ma+offset
        path = Path(v1, orgpath.codes)
        patch = PathPatch(path)
        patches.append(patch)

    pc = PatchCollection(patches, cmap=cm.get_cmap('RdYlBu_r'), edgecolors='black')
    pc.set_array(values)
    ax.add_collection(pc)
    hc.remove()

sized_hexbin(ax,hexbin)
plt.show()

proponowane rozwiązanie

Ethan
źródło
1
Czy widziałeś stackoverflow.com/questions/48844600/... ?
plasmon360
@ plasmon360 Zaktualizowałem post swoją pracą z proponowanego rozwiązania
Ethan
1
Kiedy C=df['Bin'],go użyjesz , nie pokaże gęstości, ale ilość, która jest w Binkolumnie. Fabuła jest więc poprawna. Możesz pominąć Cargument i uzyskać rozmiary oparte na gęstości.
ImportanceOfBeingErnest
@ImportanceOfBeingErnest w porządku, gotcha. Jak mogę pokolorować heksy według df ['Bin']? Chciałbym również móc zmienić minimalny rozmiar sześciokątów, aby był nieco większy, czy to możliwe?
Ethan
1
Rozmiar zależy od stosunku val/maw kodzie. Możesz go zastąpić tym, co uznasz za odpowiednie. Kolory są ustawiane za pomocą pc.set_array(values); możesz użyć czegoś innego niż valuesoczywiście.
ImportanceOfBeingErnest

Odpowiedzi:

3

Możesz poświęcić trochę czasu na zrozumienie mapowania kolorów.

    import numpy as np
    np.random.seed(3)
    import pandas as pd
    import matplotlib.pyplot as plt
    from matplotlib.collections import PatchCollection
    from matplotlib.path import Path
    from matplotlib.patches import PathPatch
    df = pd.DataFrame()
    df['X'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))
    df['Y'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))

    df['Bin'] = df.apply(lambda row: .1 if row['X'] < 30 and row['Y'] < 30 else .9, axis=1)

    #fig, ((ax1, ax2)) = plt.subplots(1, 2, sharex=True, sharey=True)
    ax1 = plt.scatter(df['X'], df['Y'])

    fig,ax2 = plt.subplots(figsize=(10,10))
    hexbin = ax2.hexbin(df['X'], df['Y'], C=df['Bin'], gridsize=20,edgecolors='black',cmap= 'RdBu', reduce_C_function=np.bincount) #**

    def sized_hexbin(ax,hc):
        offsets = hc.get_offsets()
        orgpath = hc.get_paths()[0]
        verts = orgpath.vertices
        values = hc.get_array()
        ma = values.max()
        patches = []
        for offset,val in zip(offsets,values):
            v1 = verts*val/ma + offset
            path = Path(v1, orgpath.codes)
            patch = PathPatch(path)
            patches.append(patch)

        pc = PatchCollection(patches, cmap= 'RdBu', edgecolors='black')
        pc.set_array(values)

        ax.add_collection(pc)

        hc.remove()

    sized_hexbin(ax2,hexbin)
    cb = plt.colorbar(hexbin, ax=ax2)

    plt.show()

To plot the chart based on df['bins'] values - 

Need to change the reduce_C_function in #** marked line -

    hexbin = ax2.hexbin(df['X'], df['Y'], C=df['Bin'], gridsize=20,edgecolors='black',cmap= 'RdBu', reduce_C_function=np.sum)

[![enter image description here][2]][2]


  [1]: https://i.stack.imgur.com/kv0U4.png
  [2]: https://i.stack.imgur.com/mb0gD.png

# Another variation of the chart :

# Where size is based on count of points in the bins and color is based on values of the df['bin']./ Also added if condition to control minimum hexbin size.


import numpy as np
np.random.seed(3)
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from functools import partial

mycmp = 'coolwarm'

df = pd.DataFrame()
df['X'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))
df['Y'] = list(np.random.randint(100, size=100)) + list(np.random.randint(30, size=100))

df['Bin'] = df.apply(lambda row: .1 if row['X'] < 30 and row['Y'] < 30 else .9, axis=1)

#fig, ((ax1, ax2)) = plt.subplots(1, 2, sharex=True, sharey=True)
ax1 = plt.scatter(df['X'], df['Y'])


fig,ax2 = plt.subplots(figsize=(10,10))
hexbin = ax2.hexbin(df['X'], df['Y'], C=df['Bin'], gridsize=15,edgecolors='black',cmap= newcmp , reduce_C_function=np.bincount)
hexbin2 = ax2.hexbin(df['X'], df['Y'], C=df['Bin'], gridsize=15,edgecolors='black',cmap= newcmp , reduce_C_function=np.mean)

def sized_hexbin(ax,hc,hc2):
    offsets = hc.get_offsets()
    orgpath = hc.get_paths()[0]
    verts = orgpath.vertices
    values1 = hc.get_array()
    values2 = hc2.get_array()
    ma = values1.max()
    patches = []

    for offset,val in zip(offsets,values1):
        # Adding condition for minimum size 
        if (val/ma) < 0.2:
            val_t = 0.2
        else:
            val_t = val/ma
        v1 =  verts*val_t + offset
        path = Path(v1, orgpath.codes)
        print(path)
        patch = PathPatch(path)
        patches.append(patch)

    pc = PatchCollection(patches, cmap=  newcmp)  #edgecolors='black'
    pc.set_array(values2)

    ax.add_collection(pc)
    hc.remove()
    hc2.remove()


sized_hexbin(ax2,hexbin,hexbin2)
cb = plt.colorbar(hexbin2, ax=ax2)

plt.xlim((-5, 100))
plt.ylim((-5, 100))

plt.show()

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

zagubiony w
źródło
Jak mogę zmienić kolor na podstawie df['Bin']kolumny?
Ethan
Więc nie chcesz widzieć częstotliwości w szesnastce, ale sumę wartości df ['Bin']?
lostin
Tak, chcę, aby kolor sześciokątów był oparty na df['Bin']kolumnie, więc lewe dolne heksy są niebieskie, a pozostałe są czerwone
Ethan
Dodałem fabułę na podstawie sumy df ['Bins']. Możesz edytować cmap, aby zarządzać kolorem. Nie jestem pewien, czy chcesz zrobić coś innego.
lostin
Nie chcę go pokolorować na podstawie sumy wartości w pojemniku, zamiast wartości samego pojemnika. Czy jest na to sposób? Kolory pasowałyby do kolorów z drugiej fabuły mojego przykładu
Ethan