Hex-Größe in Matplotlib-Hexbins basierend auf der Dichte benachbarter Punkte

9

Ich habe den folgenden Code, der die folgende Abbildung erzeugt

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'])

streuen

Ich habe die Daten mit Hexbins grafisch dargestellt, wie unten angegeben

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

Hexbins

Ich möchte die Größe der Sechsecke basierend auf der Dichte der Punkte ändern, die in dem Bereich eingezeichnet sind, den ein Sechseck abdeckt. Zum Beispiel sind die Sechsecke unten links (wo die Punkte kompakt sind) größer als die Sechsecke überall sonst (wo die Punkte spärlich sind). Gibt es eine Möglichkeit, dies zu tun?

Bearbeiten: Ich habe diese Lösung ausprobiert , kann aber nicht herausfinden, wie die Hexen basierend auf df ['Bin'] eingefärbt oder wie die minimale und maximale Hex-Größe eingestellt werden.

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

vorgeschlagene Lösung

Ethan
quelle
@ plasmon360 Ich habe den Beitrag mit meiner Arbeit aus der vorgeschlagenen Lösung aktualisiert
Ethan
1
Wenn Sie es verwenden C=df['Bin'],, wird nicht die Dichte angezeigt, sondern die Menge in der BinSpalte. Die Handlung ist also korrekt. Sie können CArgumente weglassen und die Größen basierend auf der Dichte ermitteln.
ImportanceOfBeingErnest
@ImportanceOfBeingErnest okay, gotcha. Wie kann ich die Felder mit dem df ['Bin'] färben? Ich möchte auch in der Lage sein, die Mindestgröße der Sechsecke etwas größer zu machen. Ist das möglich?
Ethan
1
Die Größe wird durch das Verhältnis val/maim Code bestimmt. Sie können es durch alles ersetzen, was Sie für geeignet halten. Die Farben werden über eingestellt pc.set_array(values); Sie können etwas anderes als valuesnatürlich verwenden.
ImportanceOfBeingErnest

Antworten:

3

Möglicherweise möchten Sie einige Zeit damit verbringen, die Farbzuordnung zu verstehen.

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

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

verloren
quelle
Wie kann ich die Farbe basierend auf der df['Bin']Spalte ändern ?
Ethan
Sie möchten also nicht die Häufigkeit im Hexbin sehen, sondern die Summe der df ['Bin'] -Werte?
Lostin
Ja, ich möchte, dass die Farbe der Sechsecke auf der df['Bin']Spalte basiert , also sind die unteren linken Sechsecke blau und die anderen rot
Ethan
Ich habe einen Plot hinzugefügt, der auf der Summe der df ['Bins'] basiert. Sie können cmap bearbeiten, um die Farbe zu verwalten. Ich bin mir nicht sicher, ob Sie etwas anderes tun möchten.
verloren
Ich möchte es nicht basierend auf der Summe der Werte im Bin färben, sondern auf dem Wert des Bin selbst. Gibt es eine Möglichkeit, das zu tun? Die Farben würden mit den Farben in der zweiten Darstellung meines Beispiels übereinstimmen
Ethan