首页 > 解决方案 > Matplotlib:条形顶部的线条,左侧的图例,没有样条线

问题描述

我无法调整下面的图表。

图形

数据框如下所示:

   Year  Some  All     Ratio
0  2016     9  157  0.057325
1  2017    13  189  0.068783
2  2018    21  216  0.097222
3  2019    18  190  0.094737
4  2020    28  284  0.098592

这是我想要做的:

帮助人们入门的代码如下。

data = {'Year': {0: '2016', 1: '2017', 2: '2018', 3: '2019', 4: '2020'},
 'Some': {0: 9, 1: 13, 2: 21, 3: 18, 4: 28},
 'All': {0: 157, 1: 189, 2: 216, 3: 190, 4: 284},
 'Ratio': {0: 0.05732484076433121,
  1: 0.06878306878306878,
  2: 0.09722222222222222,
  3: 0.09473684210526316,
  4: 0.09859154929577464}}

df = __import__("pandas").DataFrame(data)

ax = df.plot(x="Year", y="Ratio",
                 kind="line", linestyle='-', marker='o', color="orange",
                 figsize=((24,12))
                )
df.plot(x="Year", y="All",
            kind="bar", ax=ax, secondary_y=True
           )

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

标签: pythonpandasmatplotlib

解决方案


你问了很多事情。

  • 要在条形图的顶部绘制线条,似乎我们必须先绘制条形图,然后再绘制线条。最后画线会缩小 xlim,所以我们必须明确地应用它们。
  • 移动图例更复杂。通常你只是这样做ax1.legend(loc='upper left'),但在我们的情况下,有两个图,这似乎总是绘制第二个图例,最后绘制的图作为唯一条目。
  • 有一个文档set_bbox_to_anchor很少的功能。它定义了一些盒子 (x, y, width, height),但还有一个看似不可访问的参数控制盒子和位置的关系。“默认值是在使用 bbox_to_anchor 参数时给出不可预测的结果。” 可能需要进行一些实验。最好的办法,就是守护loclocloc="best"
  • 设置文本很简单。只需遍历 y 位置。放置在 x 位置 0,1,2,.. 并水平居中(垂直位于底部)。
  • 要移除刺,似乎有两个轴相互重叠(这也可能导致zorder无法按预期工作)。你会想要隐藏他们两个的脊椎。
  • 要删除刻度,请使用ax1.axes.yaxis.set_ticks([]).
  • 要将 ax2 刻度切换到左侧,请使用ax2.yaxis.tick_left().
import pandas as pd
from matplotlib import pyplot as plt

data = {'Year': {0: '2016', 1: '2017', 2: '2018', 3: '2019', 4: '2020'},
        'Some': {0: 9, 1: 13, 2: 21, 3: 18, 4: 28},
        'All': {0: 157, 1: 189, 2: 216, 3: 190, 4: 284},
        'Ratio': {0: 0.05732484076433121,
                  1: 0.06878306878306878,
                  2: 0.09722222222222222,
                  3: 0.09473684210526316,
                  4: 0.09859154929577464}}

df = pd.DataFrame(data)

ax1 = df.plot(x="Year", y="All",
              kind="bar",
              )
for i, a in df.All.items():
    ax1.text(i, a, str(a), ha='center', va='bottom', fontsize=18)
xlims = ax1.get_xlim()

ax2 = df.plot(x="Year", y="Ratio",
              kind="line", linestyle='-', marker='o', color="orange", ax=ax1, secondary_y=True,
              figsize=((24, 12))
              )
ax2.set_xlim(xlims)  # needed because the line plot shortens the xlims

# ax1.get_legend().set_bbox_to_anchor((0.03, 0.9, 0.1, 0.1)) # unpredictable behavior when loc='best'
# ax1.legend(loc='upper left') # in our case, this would create a second legend

ax1.get_legend().remove() # remove badly placed legend
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(handles=handles1 + handles2,   # create a new legend
           labels=labels1 + labels2,
           loc='upper left')

# ax1.yaxis.tick_right()  # place the yticks for ax1 at the right
ax2.yaxis.tick_left()  # place the yticks for ax2 at the left
ax2.set_ylabel('Ratio')
ax2.yaxis.set_label_position('left')
ax1.axes.yaxis.set_ticks([]) # remove ticks

for ax in (ax1, ax2):
    for where in ('top', 'right'):
        ax.spines[where].set_visible(False)

plt.show()

阴谋


推荐阅读