Soru matplotlib: Grup kutuları


Matplotlib içinde boxplots gruplamak için bir yolu var mı?

"A", "B" ve "C" olmak üzere üç gruba sahip olduğumuzu ve her ikisi için de "elmalar" ve "portakallar" için bir kutu plotu oluşturmak istediğimizi varsayalım. Bir gruplama doğrudan mümkün değilse, altı kombinasyonun hepsini oluşturabilir ve bunları yan yana doğrusal olarak yerleştirebiliriz. Gruplamaları görselleştirmenin en kolay yolu ne olurdu? Senaryolarımın "A +" dan çok daha uzun isimler içerdiğinden, "A + elmaları" gibi bir kene etiketini belirlemekten kaçınmaya çalışıyorum.


44
2018-05-16 15:57


Menşei




Cevaplar:


"A", "B" ve "C" yi ayırmak için "elmalar" ve "portakallar" ile boşlukları birbirinden ayırmak için renkler nasıl kullanılır?

Böyle bir şey:

from pylab import plot, show, savefig, xlim, figure, \
                hold, ylim, legend, boxplot, setp, axes

# function for setting the colors of the box plots pairs
def setBoxColors(bp):
    setp(bp['boxes'][0], color='blue')
    setp(bp['caps'][0], color='blue')
    setp(bp['caps'][1], color='blue')
    setp(bp['whiskers'][0], color='blue')
    setp(bp['whiskers'][1], color='blue')
    setp(bp['fliers'][0], color='blue')
    setp(bp['fliers'][1], color='blue')
    setp(bp['medians'][0], color='blue')

    setp(bp['boxes'][1], color='red')
    setp(bp['caps'][2], color='red')
    setp(bp['caps'][3], color='red')
    setp(bp['whiskers'][2], color='red')
    setp(bp['whiskers'][3], color='red')
    setp(bp['fliers'][2], color='red')
    setp(bp['fliers'][3], color='red')
    setp(bp['medians'][1], color='red')

# Some fake data to plot
A= [[1, 2, 5,],  [7, 2]]
B = [[5, 7, 2, 2, 5], [7, 2, 5]]
C = [[3,2,5,7], [6, 7, 3]]

fig = figure()
ax = axes()
hold(True)

# first boxplot pair
bp = boxplot(A, positions = [1, 2], widths = 0.6)
setBoxColors(bp)

# second boxplot pair
bp = boxplot(B, positions = [4, 5], widths = 0.6)
setBoxColors(bp)

# thrid boxplot pair
bp = boxplot(C, positions = [7, 8], widths = 0.6)
setBoxColors(bp)

# set axes limits and labels
xlim(0,9)
ylim(0,9)
ax.set_xticklabels(['A', 'B', 'C'])
ax.set_xticks([1.5, 4.5, 7.5])

# draw temporary red and blue lines and use them to create a legend
hB, = plot([1,1],'b-')
hR, = plot([1,1],'r-')
legend((hB, hR),('Apples', 'Oranges'))
hB.set_visible(False)
hR.set_visible(False)

savefig('boxcompare.png')
show()

grouped box plot


71
2018-05-16 22:07



Bu çok güzel bir çözüm çünkü hem renklere göre hem de pozisyonlara göre gruplama yapıyorsunuz! Görünüşte yerleşik bir işlevsellik yokmuş gibi göründüğünden, tam olarak aradığım şey bu. Çok teşekkür ederim! - bluenote10
Bu örnek, matplotlib 1.3.1 ile mükemmel bir şekilde çalışıyor ancak 1.4.0 değil github.com/matplotlib/matplotlib/issues/3544 (Seçtiğiniz verilerin problem çıkmayacak şekilde bir aykırı olmasına rağmen, erişirken hala bir hatayla karşılaşacaksınız. bp['fliers'][2]). - anonymous
Pandalarda, sadece bir renk özelliği vererek kutuların rengini ayarlamak mümkündür: data.plot(kind='box',color='blue') - Peter9192
El ilanları ile ilgili olarak, şimdi olmalıdır: plt.setp(bp['fliers'][0], markeredgecolor='blue') ve plt.setp(bp['fliers'][1], markeredgecolor='red') - John Manak


Basit bir şekilde kullanmak olurdu pandalar. Bir örnek uyarladım çizim belgeleme:

In [1]: import pandas as pd, numpy as np

In [2]: df = pd.DataFrame(np.random.rand(12,2), columns=['Apples', 'Oranges'] )

In [3]: df['Categories'] = pd.Series(list('AAAABBBBCCCC'))

In [4]: pd.options.display.mpl_style = 'default'

In [5]: df.boxplot(by='Categories')
Out[5]: 
array([<matplotlib.axes.AxesSubplot object at 0x51a5190>,
       <matplotlib.axes.AxesSubplot object at 0x53fddd0>], dtype=object)

pandas boxplot


28
2018-05-17 06:36



Çok teşekkür ederim! Bu da çok ilginç bir öneri! - bluenote10
Bu meyvenin tersinin nasıl yapılacağını anlayamıyorum - her meyveye göre, gruplara göre gruplandırılmış (molly's annwer ile aynı gruplandırma). Bir yolu var mı? - naught101
"Ters" ne olmalıdır emin değilim. Molly'nin cevabından (sadece bir altbölüm) tam olarak ne tür bir arsa anlamına gelirse, bu bir panda çizme komutuyla mümkün değildir. Matplotlib ve daha karmaşık bir betik kullanmanız gerekir. - bmu


İşte benim versiyonum. Verileri kategorilere göre depolar.

import matplotlib.pyplot as plt
import numpy as np

data_a = [[1,2,5], [5,7,2,2,5], [7,2,5]]
data_b = [[6,4,2], [1,2,5,3,2], [2,3,5,1]]

ticks = ['A', 'B', 'C']

def set_box_color(bp, color):
    plt.setp(bp['boxes'], color=color)
    plt.setp(bp['whiskers'], color=color)
    plt.setp(bp['caps'], color=color)
    plt.setp(bp['medians'], color=color)

plt.figure()

bpl = plt.boxplot(data_a, positions=np.array(xrange(len(data_a)))*2.0-0.4, sym='', widths=0.6)
bpr = plt.boxplot(data_b, positions=np.array(xrange(len(data_b)))*2.0+0.4, sym='', widths=0.6)
set_box_color(bpl, '#D7191C') # colors are from http://colorbrewer2.org/
set_box_color(bpr, '#2C7BB6')

# draw temporary red and blue lines and use them to create a legend
plt.plot([], c='#D7191C', label='Apples')
plt.plot([], c='#2C7BB6', label='Oranges')
plt.legend()

plt.xticks(xrange(0, len(ticks) * 2, 2), ticks)
plt.xlim(-2, len(ticks)*2)
plt.ylim(0, 8)
plt.tight_layout()
plt.savefig('boxcompare.png')

İtibarım çok kısaydı, bu yüzden buraya görüntü gönderemiyorum. Onu çalıştırabilir ve sonucu görebilirsin. Temelde Molly'nin yaptığıyla çok benzer.

Kullandığınız python sürümüne bağlı olarak, değiştirmeniz gerekebilir. xrange ile range

Result of this code


20
2017-11-21 21:46



Değişkenlerinizi 'mu' ve 'alpha' kullanmıyorsunuz gibi görünüyor. Aksi halde çözümünüzü gerçekten çok seviyorum çünkü evrensel bir çözüme çok yakın, kodun ihtiyaçlarının ayarlanmasıyla sadece arsada gruplanan kategori sayısı. - Horstinator
Bu, Molly'nin önerdiğinden çok daha hoş ve daha sağlam. - durbachit
Bu sayfadaki tüm cevaplar arasında en iyi çözüm imo. @Horstinator'ın işaret ettiği gibi, elma ve portakalda aynı sayıda örnek gerektirmez. - Chris
Bu en iyi cevap! Sadece 2'den fazla grup için esnek hale getirmek için bir şey olurdu - Kuzeko


Sadece sohbete eklemek için, nesnenin sözlüğünü tekrarlayarak kutu çiziminin rengini değiştirmenin daha zarif bir yolunu buldum.

import numpy as np
import matplotlib.pyplot as plt

def color_box(bp, color):

    # Define the elements to color. You can also add medians, fliers and means
    elements = ['boxes','caps','whiskers']

    # Iterate over each of the elements changing the color
    for elem in elements:
        [plt.setp(bp[elem][idx], color=color) for idx in xrange(len(bp[elem]))]
    return

a = np.random.uniform(0,10,[100,5])    

bp = plt.boxplot(a)
color_box(bp, 'red')

Original box plot

Modified box plot

Şerefe!


3
2018-03-03 17:24





Veri alanı:

df = pd.DataFrame({'Group':['A','A','A','B','C','B','B','C','A','C'],\
                  'Apple':np.random.rand(10),'Orange':np.random.rand(10)})
df = df[['Group','Apple','Orange']]

        Group    Apple     Orange
    0      A  0.465636  0.537723
    1      A  0.560537  0.727238
    2      A  0.268154  0.648927
    3      B  0.722644  0.115550
    4      C  0.586346  0.042896
    5      B  0.562881  0.369686
    6      B  0.395236  0.672477
    7      C  0.577949  0.358801
    8      A  0.764069  0.642724
    9      C  0.731076  0.302369

Bu araziler için Seaborn kütüphanesini kullanabilirsiniz. İlk melt Verileri biçimlendirmek için veri çerçevesi ve daha sonra seçtiğiniz kutu kutusunu oluşturun.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
dd=pd.melt(df,id_vars=['Group'],value_vars=['Apple','Orange'],var_name='fruits')
sns.boxplot(x='Group',y='value',data=dd,hue='fruits')

enter image description here


3
2018-06-23 04:52





İşte Molly'nin kodunu ve internette bulduğum diğer kodları gruplandırılmış boxplots oluşturmak için yazdığım bir işlev:

import numpy as np
import matplotlib.pyplot as plt

def custom_legend(colors, labels, linestyles=None):
    """ Creates a list of matplotlib Patch objects that can be passed to the legend(...) function to create a custom
        legend.

    :param colors: A list of colors, one for each entry in the legend. You can also include a linestyle, for example: 'k--'
    :param labels:  A list of labels, one for each entry in the legend.
    """

    if linestyles is not None:
        assert len(linestyles) == len(colors), "Length of linestyles must match length of colors."

    h = list()
    for k,(c,l) in enumerate(zip(colors, labels)):
        clr = c
        ls = 'solid'
        if linestyles is not None:
            ls = linestyles[k]
        patch = patches.Patch(color=clr, label=l, linestyle=ls)
        h.append(patch)
    return h


def grouped_boxplot(data, group_names=None, subgroup_names=None, ax=None, subgroup_colors=None,
                    box_width=0.6, box_spacing=1.0):
    """ Draws a grouped boxplot. The data should be organized in a hierarchy, where there are multiple
        subgroups for each main group.

    :param data: A dictionary of length equal to the number of the groups. The key should be the
                group name, the value should be a list of arrays. The length of the list should be
                equal to the number of subgroups.
    :param group_names: (Optional) The group names, should be the same as data.keys(), but can be ordered.
    :param subgroup_names: (Optional) Names of the subgroups.
    :param subgroup_colors: A list specifying the plot color for each subgroup.
    :param ax: (Optional) The axis to plot on.
    """

    if group_names is None:
        group_names = data.keys()

    if ax is None:
        ax = plt.gca()
    plt.sca(ax)

    nsubgroups = np.array([len(v) for v in data.values()])
    assert len(np.unique(nsubgroups)) == 1, "Number of subgroups for each property differ!"
    nsubgroups = nsubgroups[0]

    if subgroup_colors is None:
        subgroup_colors = list()
        for k in range(nsubgroups):
            subgroup_colors.append(np.random.rand(3))
    else:
        assert len(subgroup_colors) == nsubgroups, "subgroup_colors length must match number of subgroups (%d)" % nsubgroups

    def _decorate_box(_bp, _d):
        plt.setp(_bp['boxes'], lw=0, color='k')
        plt.setp(_bp['whiskers'], lw=3.0, color='k')

        # fill in each box with a color
        assert len(_bp['boxes']) == nsubgroups
        for _k,_box in enumerate(_bp['boxes']):
            _boxX = list()
            _boxY = list()
            for _j in range(5):
                _boxX.append(_box.get_xdata()[_j])
                _boxY.append(_box.get_ydata()[_j])
            _boxCoords = zip(_boxX, _boxY)
            _boxPolygon = plt.Polygon(_boxCoords, facecolor=subgroup_colors[_k])
            ax.add_patch(_boxPolygon)

        # draw a black line for the median
        for _k,_med in enumerate(_bp['medians']):
            _medianX = list()
            _medianY = list()
            for _j in range(2):
                _medianX.append(_med.get_xdata()[_j])
                _medianY.append(_med.get_ydata()[_j])
                plt.plot(_medianX, _medianY, 'k', linewidth=3.0)

            # draw a black asterisk for the mean
            plt.plot([np.mean(_med.get_xdata())], [np.mean(_d[_k])], color='w', marker='*',
                      markeredgecolor='k', markersize=12)

    cpos = 1
    label_pos = list()
    for k in group_names:
        d = data[k]
        nsubgroups = len(d)
        pos = np.arange(nsubgroups) + cpos
        label_pos.append(pos.mean())
        bp = plt.boxplot(d, positions=pos, widths=box_width)
        _decorate_box(bp, d)
        cpos += nsubgroups + box_spacing

    plt.xlim(0, cpos-1)
    plt.xticks(label_pos, group_names)

    if subgroup_names is not None:
        leg = custom_legend(subgroup_colors, subgroup_names)
        plt.legend(handles=leg)

Bu işlevi (işlevleri) kullanabilirsiniz:

data = { 'A':[np.random.randn(100), np.random.randn(100) + 5],
         'B':[np.random.randn(100)+1, np.random.randn(100) + 9],
         'C':[np.random.randn(100)-3, np.random.randn(100) -5]
       }

grouped_boxplot(data, group_names=['A', 'B', 'C'], subgroup_names=['Apples', 'Oranges'], subgroup_colors=['#D02D2E', '#D67700'])
plt.show()

1
2017-12-29 01:59