Matplotlibを使用してドットプロットを描画する場合、重複するデータポイントをオフセットして、それらがすべて表示されるようにします。たとえば、私が持っている場合
CategoryA: 0,0,3,0,5
CategoryB: 5,10,5,5,10
CategoryA
"0"データポイントのそれぞれは、CategoryB
とは別個のままで、互いの上に並べるのではなく、並べて設定する必要があります。
R(ggplot2
)には、これを行う"jitter"
オプションがあります。 matplotlibに同様のオプションがありますか、または同様の結果につながる別のアプローチがありますか?
Edit:明確にするために、 Rの"beeswarm"
プロット =は基本的に私が念頭に置いているものであり、 pybeeswarm
はmatplotlib/Pythonバージョンからの初期段階ではあるが有用な開始点です。
Edit:Seabornの Swarmplot を追加して、バージョン0.7で導入、私が望んでいたものの優れた実装です。
@ 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および最大値であると想定しています。
その後、jitter
の代わりに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()
問題にアプローチする1つの方法は、散布図/ドット/蜂の群れプロットの各「行」をヒストグラムのビンと考えることです。
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')
これには明らかにデータのビニングが含まれるため、精度がいくらか失われる可能性があります。個別のデータがある場合は、次のものを置き換えることができます。
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()
この2番目の方法は、大まかに violin plots の動作に基づいています。それでもポイントが重複していないことを保証することはできませんが、実際には、適切な数のポイント(> 20)があればかなり見栄えの良い結果が得られる傾向があり、分布は合理的に近似できることがわかりますガウスの合計によって。
Seabornは sns.swarmplot()
を介してヒストグラムのようなカテゴリドットプロットを提供し、 sns.stripplot()
を介してジッタ付きカテゴリドットプロットを提供します。
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()
ここで直接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()
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%