복붙노트

[PYTHON] Matplotlib : "scatter / dot / beeswarm"플롯에서 중첩되는 데이터 포인트 피하기

PYTHON

Matplotlib : "scatter / dot / beeswarm"플롯에서 중첩되는 데이터 포인트 피하기

matplotlib를 사용하여 도트 플롯을 그릴 때 겹치는 데이터 포인트를 모두 표시하여 모두 보이게하고 싶습니다. 예를 들어, 내가 가지고있는 경우

CategoryA: 0,0,3,0,5  
CategoryB: 5,10,5,5,10  

각 카테고리 "0"데이터 포인트가 카테고리 B와 구별되는 동시에 서로 위에 놓이 아니라 나란히 놓기를 원합니다.

R (ggplot2)에는이 작업을 수행하는 "지터"옵션이 있습니다. 비슷한 옵션이 matplotlib에 있습니까? 아니면 유사한 결과로 이어질 수있는 또 다른 접근 방식이 있습니까?

편집 : 명확히하기 위해 R의 "beeswarm"줄거리는 본질적으로 내가 염두에두고있는 것으로, pybeeswarm은 matplotlib / Python 버전에서 일찍부터 유용한 시작이다.

편집 : 0.7 버전에서 소개 된 Seaborn의 Swarmplot을 추가하면 내가 원하는 것을 훌륭하게 구현할 수 있습니다.

해결법

  1. ==============================

    1.@ user2467675로 답변을 확장하면 다음과 같습니다.

    @ user2467675로 답변을 확장하면 다음과 같습니다.

    def rand_jitter(arr):
        stdev = .01*(max(arr)-min(arr))
        return arr + np.random.randn(len(arr)) * stdev
    
    def jitter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, hold=None, **kwargs):
        return scatter(rand_jitter(x), rand_jitter(y), s=s, c=c, marker=marker, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, hold=hold, **kwargs)
    

    stdev 변수는 지터가 다른 비율로 표시되기에 충분한 지 확인하지만 축의 한계가 0이고 최대 값이라고 가정합니다.

    그러면 분산 대신 지터를 호출 할 수 있습니다.

  2. ==============================

    2.numpy.random을 사용하여 X 축을 따라 데이터를 분산시키고 각 범주별로 고정 된 점을 기준으로 데이터를 분산시킨 다음 기본적으로 각 범주에 대해 pyplot.scatter ()를 수행합니다.

    numpy.random을 사용하여 X 축을 따라 데이터를 분산시키고 각 범주별로 고정 된 점을 기준으로 데이터를 분산시킨 다음 기본적으로 각 범주에 대해 pyplot.scatter ()를 수행합니다.

    import matplotlib.pyplot as plt
    import numpy as np
    
    #random data for category A, B, with B "taller"
    yA, yB = np.random.randn(100), 5.0+np.random.randn(1000)
    
    xA, xB = np.random.normal(1, 0.1, len(yA)), 
             np.random.normal(3, 0.1, len(yB))
    
    plt.scatter(xA, yA)
    plt.scatter(xB, yB)
    plt.show()
    

  3. ==============================

    3.이 문제에 접근하는 한 가지 방법은 스 캐터 / 도트 / 벌집의 각 '행'을 히스토그램의 빈으로 생각하는 것입니다.

    이 문제에 접근하는 한 가지 방법은 스 캐터 / 도트 / 벌집의 각 '행'을 히스토그램의 빈으로 생각하는 것입니다.

    data = np.random.randn(100)
    
    width = 0.8     # the maximum width of each 'row' in the scatter plot
    xpos = 0        # the centre position of the scatter plot in x
    
    counts, edges = np.histogram(data, bins=20)
    
    centres = (edges[:-1] + edges[1:]) / 2.
    yvals = centres.repeat(counts)
    
    max_offset = width / counts.max()
    offsets = np.hstack((np.arange(cc) - 0.5 * (cc - 1)) for cc in counts)
    xvals = xpos + (offsets * max_offset)
    
    fig, ax = plt.subplots(1, 1)
    ax.scatter(xvals, yvals, s=30, c='b')
    

    이것은 분명히 데이터 비닝 (binning)을 포함하므로 정확도를 잃을 수도 있습니다. 개별 데이터가있는 경우 다음을 대체 할 수 있습니다.

    counts, edges = np.histogram(data, bins=20)
    centres = (edges[:-1] + edges[1:]) / 2.
    

    와:

    centres, counts = np.unique(data, return_counts=True)
    

    연속적인 데이터에 대해서조차도 정확한 y 좌표를 보존하는 또 다른 접근법은 커널 밀도 추정을 사용하여 x 축의 무작위 지터의 진폭을 조정하는 것입니다.

    from scipy.stats import gaussian_kde
    
    kde = gaussian_kde(data)
    density = kde(data)     # estimate the local density at each datapoint
    
    # generate some random jitter between 0 and 1
    jitter = np.random.rand(*data.shape) - 0.5 
    
    # scale the jitter by the KDE estimate and add it to the centre x-coordinate
    xvals = 1 + (density * jitter * width * 2)
    
    ax.scatter(xvals, data, s=30, c='g')
    for sp in ['top', 'bottom', 'right']:
        ax.spines[sp].set_visible(False)
    ax.tick_params(top=False, bottom=False, right=False)
    
    ax.set_xticks([0, 1])
    ax.set_xticklabels(['Histogram', 'KDE'], fontsize='x-large')
    fig.tight_layout()
    

    이 두 번째 방법은 바이올린 플롯이 어떻게 작동하는지에 근거합니다. 어떤 점도 겹치지 않는다는 것을 보장 할 수는 없지만 실제로는 점 수 (> 20)가있는 한 실제로 꽤 멋진 결과를 얻는 경향이 있으며 분배는 합리적으로 잘 근사 될 수 있습니다. Gaussians의 합계.

  4. ==============================

    4.여기에 직접 mpl 대안을 알지 못하면 매우 기초적인 제안이 있습니다.

    여기에 직접 mpl 대안을 알지 못하면 매우 기초적인 제안이 있습니다.

    from matplotlib import pyplot as plt
    from itertools import groupby
    
    CA = [0,4,0,3,0,5]  
    CB = [0,0,4,4,2,2,2,2,3,0,5]  
    
    x = []
    y = []
    for indx, klass in enumerate([CA, CB]):
        klass = groupby(sorted(klass))
        for item, objt in klass:
            objt = list(objt)
            points = len(objt)
            pos = 1 + indx + (1 - points) / 50.
            for item in objt:
                x.append(pos)
                y.append(item)
                pos += 0.04
    
    plt.plot(x, y, 'o')
    plt.xlim((0,3))
    
    plt.show()
    

  5. ==============================

    5.Seaborn은 sns.swarmplot () 및 sns.stripplot ()을 통해 jittered 범주 형 점 - 플롯을 통해 막대 그래프와 같은 범주 형 점 플롯을 제공합니다.

    Seaborn은 sns.swarmplot () 및 sns.stripplot ()을 통해 jittered 범주 형 점 - 플롯을 통해 막대 그래프와 같은 범주 형 점 플롯을 제공합니다.

    import seaborn as sns
    
    sns.set(style='ticks', context='talk')
    iris = sns.load_dataset('iris')
    
    sns.swarmplot('species', 'sepal_length', data=iris)
    sns.despine()
    

    sns.stripplot('species', 'sepal_length', data=iris, jitter=0.2)
    sns.despine()
    

  6. ==============================

    6.Seaborn의 swarmplot은 당신이 염두에두고있는 것에 가장 잘 어울리는 것 같지만 Seaborn의 regplot과도 지터를 낼 수 있습니다 :

    Seaborn의 swarmplot은 당신이 염두에두고있는 것에 가장 잘 어울리는 것 같지만 Seaborn의 regplot과도 지터를 낼 수 있습니다 :

    import seaborn as sns
    iris = sns.load_dataset('iris')
    
    sns.regplot(x='sepal_length',
                y='sepal_width',
                data=iris,
                fit_reg=False,  # do not fit a regression line
                x_jitter=0.1,  # could also dynamically set this with range of data
                y_jitter=0.1,
                scatter_kws={'alpha': 0.5})  # set transparency to 50%
    
  7. from https://stackoverflow.com/questions/8671808/matplotlib-avoiding-overlapping-datapoints-in-a-scatter-dot-beeswarm-plot by cc-by-sa and MIT license