我正在生成小提琴图,并希望在分布的中位数显示一条线,中位数上方和下方的区域使用不同的颜色。这是MVCE:
import numpy as np
import matplotlib.pyplot as plt
import seaborn
np.random.seed(1)
d1 = np.random.normal(size=5000)
d2 = np.random.normal(scale=0.5, size=5000)
x = d1 + d2
plt.figure(figsize=(5, 5))
seaborn.violinplot(y=x)
这是结果图:
以及我想创建的输出:
我已经搜索了一段时间,但似乎找不到任何文档或示例。可以在matplotlib或seaborn(或Python中的任何其他绘图库)中完成吗?
答案 0 :(得分:2)
我对结果并不完全满意,但这是我的追求。
我使用的是matplotlib
的{{1}}版本,而不是violinplot()
,因为前者会返回一个字典,其中包含制作的各种Artist,尽管seaborn也可以这样做花费更多的精力来定位正确的seaborn
对象。
实际上使用Collection
绘制小提琴图,从中可以提取顶点的坐标。有了这些,只需选择高于或低于中值的坐标,然后创建一个新的PolyCollection
即可添加到轴上。最后,我删除了原始艺术家。
我对结果并不完全满意,因为如此创建的两位艺术家没有接触。这是因为我们缺少最初将底部连接到顶部的顶点。如果这对您来说是个问题,则可以通过在与另一个集合中的一个匹配的集合顶点坐标中的任意一个的开始和结尾处添加新坐标来解决此问题,从而填补空白。
PolyCollection
答案 1 :(得分:1)
我已经准备好解决方案,但是现在看到@DizietAsahi发布了类似的解决方案。我仍将其张贴在这里,仅指出差异。
通常,您想买几把小提琴。因此,最好将所有内容放入一个循环中。该循环可以存在于函数中。该功能可直接用于小提琴的样式。现在,与现有解决方案相反,我将创建两个小提琴图,并从每个小提琴图中切下上部或下部。然后看起来像
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
d1 = np.random.normal(size=5000)
d2 = np.random.normal(scale=0.2, size=5000)
x = [d1+1, d1 + d2, d2-0.5]
fig, ax = plt.subplots()
violin1 = ax.violinplot(x, showmedians=True, showextrema=False, points=300)
violin2 = ax.violinplot(x, showmedians=True, showextrema=False, points=300)
def cut_violin_at_median(violin, cut_above=True, **kwargs):
for i in range(len(violin["bodies"])):
median = violin["cmedians"].get_paths()[i].vertices[0,1]
pthcol = violin["bodies"][i]
v = pthcol.get_paths()[0].vertices
if cut_above:
ind = v[:,1] <= median
else:
ind = v[:,1] > median
pthcol.set_verts([v[ind]])
pthcol.set(**kwargs)
cut_violin_at_median(violin1, cut_above=True, color="crimson")
cut_violin_at_median(violin2, cut_above=False, color="limegreen")
plt.show()
请注意,为了在小提琴的两个部分之间没有较大的差距,可以增加执行内核密度估计的点数。在这里,我使用300,但也许更高的数字还是有用的。