双Y轴的双柱状图

69 投票
5 回答
145516 浏览
提问于 2025-04-18 09:32

我有一个数据表,长得像这样:

     amount     price
age
A     40929   4066443
B     93904   9611272
C    188349  19360005
D    248438  24335536
E    205622  18888604
F    140173  12580900
G     76243   6751731
H     36859   3418329
I     29304   2758928
J     39768   3201269
K     30350   2867059

现在我想画一个柱状图,把年龄放在横轴上作为标签。每个横坐标上应该有两根柱子,一根表示数量,另一根表示价格。我可以简单地用以下代码实现:

df.plot(kind='bar')

问题是比例不对。价格太高了,以至于我在图上看不清数量,看看这个:

在这里输入图片描述

所以我想要一个第二个纵轴。我试着用以下代码:

df.loc[:,'amount'].plot(kind='bar')
df.loc[:,'price'].plot(kind='bar',secondary_y=True)

但这样会把柱子覆盖掉,并没有把它们并排放置。有没有办法做到这一点,而不需要去使用更底层的matplotlib(当然可以通过手动把柱子并排放置来实现)?

目前,我在子图中使用了两个单独的图:

df.plot(kind='bar',grid=True,subplots=True,sharex=True); 

结果是:

在这里输入图片描述

5 个回答

0

有时候,使用 plt.bar(x,y) 会让你有更多的灵活性:

fig, ax = plt.subplots(1,1, figsize=(10, 8))
width = 0.4 # Width of a bar 

ax2 = ax.twinx()

vals=df['series1']
x=list(df['xseries1'])[:]
bar1 = ax.bar(x, vals, width = width,color='darkblue')

vals2 = df['series2']
x2=list(df['xseries1']+width)[:]
bar2 = ax2.bar(x2, vals2, width = width,color='darkorange')

ax.set_ylabel("whatever1",color='darkblue')
ax2.set_ylabel("whatever2",color='darkorange')
ax.set_xlabel("Fiscal Year")

#change y limits to give more room:
scale=1.1
max_y_lim = max(vals)*scale
min_y_lim = min(vals)
ax.set_ylim(min_y_lim, max_y_lim);
max_y_lim2 = max(vals2)*scale
min_y_lim = min(vals2)
ax2.set_ylim(min_y_lim2, max_y_lim2)

plt.show()
6

正如InLaw提到的,你应该使用 secondary_y = 'amount'

为了补充他的回答,这里是如何为两个坐标轴设置y轴标签的方法:

df.plot.bar(figsize=(15,5), secondary_y= 'amount')

ax1, ax2 = plt.gcf().get_axes() # gets the current figure and then the axes

ax1.set_ylabel('price')

ax2.set_ylabel('amount')
9

这里有另一种方法:

  • 在左侧的坐标轴上创建所有的条形图
  • 通过改变某些条形图的 transform 属性,把它们移动到右侧的坐标轴上

下面是代码:

import pylab as pl
df = pd.DataFrame(np.random.rand(10, 2), columns=["left", "right"])
df["left"] *= 100

ax = df.plot(kind="bar")
ax2 = ax.twinx()
for r in ax.patches[len(df):]:
    r.set_transform(ax2.transData)
ax2.set_ylim(0, 2);

这是输出结果:

在这里输入图片描述

94

你只需要写:df.plot( kind= 'bar', secondary_y= 'amount')

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from io import StringIO
s = StringIO("""     amount     price
A     40929   4066443
B     93904   9611272
C    188349  19360005
D    248438  24335536
E    205622  18888604
F    140173  12580900
G     76243   6751731
H     36859   3418329
I     29304   2758928
J     39768   3201269
K     30350   2867059""")
df = pd.read_csv(s, index_col=0, delimiter=' ', skipinitialspace=True)

_ = df.plot( kind= 'bar' , secondary_y= 'amount' , rot= 0 )
plt.show()

Secondary_Y_axis

108

使用新的 pandas 版本(0.14.0 或更高版本),下面的代码就可以运行了。为了创建两个坐标轴,我手动创建了两个 matplotlib 的坐标轴对象(axax2),它们将用于两个柱状图。

在绘制 DataFrame 时,你可以通过 ax=... 来选择坐标轴对象。此外,为了防止两个图重叠,我修改了它们的对齐方式,使用了 position 这个参数,默认值是 0.5,但这样会导致两个柱状图重叠在一起。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from io import StringIO

s = StringIO("""     amount     price
A     40929   4066443
B     93904   9611272
C    188349  19360005
D    248438  24335536
E    205622  18888604
F    140173  12580900
G     76243   6751731
H     36859   3418329
I     29304   2758928
J     39768   3201269
K     30350   2867059""")

df = pd.read_csv(s, index_col=0, delimiter=' ', skipinitialspace=True)

fig = plt.figure() # Create matplotlib figure

ax = fig.add_subplot(111) # Create matplotlib axes
ax2 = ax.twinx() # Create another axes that shares the same x-axis as ax.

width = 0.4

df.amount.plot(kind='bar', color='red', ax=ax, width=width, position=1)
df.price.plot(kind='bar', color='blue', ax=ax2, width=width, position=0)

ax.set_ylabel('Amount')
ax2.set_ylabel('Price')

plt.show()

Plot

撰写回答