简体   繁体   English

Matplotlib:条形图顶部的线条,左侧的图例,无样条曲线

[英]Matplotlib: Lines on Top of Bars, Legend on the Left, No Splines

I'm having trouble tweaking the graph below.我在调整下图时遇到问题。

图形

Here's what the dataframe looks like:这是数据框的样子:

   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

Here's what I want to do:这是我想要做的:

  • The orange line should be in front of the bars.橙色线应该在条形图的前面。 I tried using the zorder parameter and it didn't help.我尝试使用zorder参数但没有帮助。 I also tried switch the order of the axes object, but I couldn't get the line to be assigned to the primary axis.我也试过切换轴对象的顺序,但我无法将线分配给主轴。
  • I want the legend on the left side.我想要左侧的图例。 You'll notice in the code below that I'm using a somewhat large figsize argument.您会在下面的代码中注意到我使用了一个有点大的figsize参数。 If I use a smaller one, the legend will magically move to the left, but I don't want to use a smaller one.如果我使用较小的,图例会神奇地向左移动,但我不想使用较小的。
  • I want to label the bar graphs on top of each bar with its corresponding value.我想用相应的值标记每个条形图顶部的条形图。 I tried iterating over each value and individually annotating the bars with ax.annotate , but I couldn't center the values automatically.我尝试遍历每个值并使用ax.annotate单独注释条形图,但我无法自动将这些值居中。 In this minimal example, all the values are three digits long, but in the original data I have numbers that four digits long and I couldn't find a good way to make it centered for all of them.在这个最小的例子中,所有的值都是三位数长,但在原始数据中,我有四位数长的数字,我找不到一个好方法让它为所有这些数字居中。
  • Finally, I want to get rid of the top and right spines.最后,我想去掉顶部和右侧的书脊。 My code below didn't remove them for some reason.由于某种原因,我下面的代码没有删除它们。

The code to help people get started follows below.下面是帮助人们入门的代码。

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)

Your asking quite a number of things.你问了很多事情。

  • To get the line on top of the bar, it seems we have to first draw the bars and afterwards the line.要在条形图顶部画线,似乎我们必须先画条形图,然后再画线。 Drawing the line last shrinks the xlims, so we have to apply them explicitely.最后画线会缩小 xlims,所以我们必须明确地应用它们。
  • Moving the legend is more complicated.移动图例比较复杂。 Normally you just do ax1.legend(loc='upper left') , but in our case with two plots this seems to always draw a second legend with the last drawn plot as only entry.通常你只需执行ax1.legend(loc='upper left') ,但在我们有两个图的情况下,这似乎总是绘制第二个图例,最后绘制的图作为唯一条目。
  • There is a function set_bbox_to_anchor with little documentation .有一个函数set_bbox_to_anchor文档很少。 It defines some box (x, y, width, height) , but there is also a seemingly inaccessible loc parameter that controls how the box and the position relate.它定义了一些盒子 (x, y, width, height) ,但还有一个看似不可访问的loc参数控制盒子和位置的关系。 "The default for loc is loc="best" which gives unpredictable results when the bbox_to_anchor argument is used." loc的默认值是loc="best" ,当使用 bbox_to_anchor 参数时会产生不可预测的结果。” Some experimentation might be needed.可能需要进行一些实验。 The best solution, is to guard the最好的解决办法,就是守护
  • Setting the text is simple.设置文本很简单。 Just iterate over the y positions.只需遍历 y 位置。 Place at x position 0,1,2,.. and center horizontally (vertically at bottom).放置在 x 位置 0,1,2,.. 并水平居中(垂直在底部)。
  • To remove the spines, it seems there are two axes over each other (what probably also causes the zorder not to work as desired).要移除脊椎,似乎有两个轴相互重叠(这也可能导致zorder无法按预期工作)。 You'll want to hide the spines of both of them.你会想要隐藏它们的脊椎。
  • To remove the ticks, use ax1.axes.yaxis.set_ticks([]) .要删除刻度,请使用ax1.axes.yaxis.set_ticks([])
  • To switch the ax2 ticks to the left use ax2.yaxis.tick_left() .要将 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()

阴谋

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM