pandas 带有两个 y 轴的 Seaborn 条形图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51882279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 05:57:05  来源:igfitidea点击:

Seaborn barplot with two y-axis

python-3.xpandasmatplotlibseaborn

提问by Flo1895

considering the following pandas DataFrame:

考虑以下Pandas数据帧:

          labels  values_a  values_b  values_x  values_y
  0       date1      1         3        150       170
  1       date2      2         6        200       180

It is easy to plot this with Seaborn (see example code below). However, due to the big difference between values_a/values_b and values_x/values_y, the bars for values_a and values_b are not easily visible (actually, the dataset given above is just a sample and in my real dataset the difference is even bigger). Therefore, I would like to use two y-axis, i.e., one y-axis for values_a/values_b and one for values_x/values_y. I tried to use plt.twinx() to get a second axis but unfortunately, the plot shows only two bars for values_x and values_y, even though there are at least two y-axis with the right scaling. :) Do you have an idea how to fix that and get four bars for each label whereas the values_a/values_b bars relate to the left y-axis and the values_x/values_y bars relate to the right y-axis?

使用 Seaborn 很容易绘制此图(请参见下面的示例代码)。然而,由于 values_a/values_b 和 values_x/values_y 之间的巨大差异,values_a 和 values_b 的条形不容易看到(实际上,上面给出的数据集只是一个样本,在我的真实数据集中差异更大)。因此,我想使用两个 y 轴,即一个 y 轴用于 values_a/values_b,一个用于 values_x/values_y。我尝试使用 plt.twinx() 来获取第二个轴,但不幸的是,该图仅显示了 values_x 和 values_y 的两个条形,即使至少有两个 y 轴具有正确的缩放比例。:) 您是否知道如何解决这个问题并为每个标签获得四个条形,而 values_a/values_b 条形与左侧 y 轴相关,而 values_x/values_y 条形与右侧 y 轴相关?

Thanks in advance!

提前致谢!

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
                                       ("date2", 2, 6, 200, 180)],\
                                       columns=columns)

# working example but with unreadable values_a and values_b
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
                           var_name="source", value_name="value_numbers")
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
                data=test_data_melted)
plt.show()

# values_a and values_b are not displayed
values1_melted = pd.melt(test_data, id_vars=columns[0],\
                         value_vars=["values_a", "values_b"],\
                         var_name="source1", value_name="value_numbers1")
values2_melted = pd.melt(test_data, id_vars=columns[0],\
                         value_vars=["values_x", "values_y"],\
                         var_name="source2", value_name="value_numbers2")
g1 = sns.barplot(x=columns[0], y="value_numbers1", hue="source1",\
                 data=values1_melted)
ax2 = plt.twinx()
g2 = sns.barplot(x=columns[0], y="value_numbers2", hue="source2",\
                 data=values2_melted, ax=ax2)
plt.show()

Working examplevalues_a/values_b missing

工作示例values_a/values_b 缺失

采纳答案by ALollz

This is probably best suited for multiple sub-plots, but if you are truly set on a single plot, you can scale the data before plotting, create another axis and then modify the tick values.

这可能最适合多个子图,但如果您真的设置在单个图上,则可以在绘图前缩放数据,创建另一个轴,然后修改刻度值。

Sample Data

样本数据

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import numpy as np

columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
                                       ("date2", 2, 6, 200, 180)],\
                                       columns=columns)

test_data_melted = pd.melt(test_data, id_vars=columns[0],\
                           var_name="source", value_name="value_numbers")

Code:

代码:

# Scale the data, just a simple example of how you might determine the scaling
mask = test_data_melted.source.isin(['values_a', 'values_b'])
scale = int(test_data_melted[~mask].value_numbers.mean()
            /test_data_melted[mask].value_numbers.mean())
test_data_melted.loc[mask, 'value_numbers'] = test_data_melted.loc[mask, 'value_numbers']*scale

# Plot
fig, ax1 = plt.subplots()
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
                data=test_data_melted, ax=ax1)

# Create a second y-axis with the scaled ticks
ax1.set_ylabel('X and Y')
ax2 = ax1.twinx()

# Ensure ticks occur at the same positions, then modify labels
ax2.set_ylim(ax1.get_ylim())
ax2.set_yticklabels(np.round(ax1.get_yticks()/scale,1))
ax2.set_ylabel('A and B')

plt.show()

enter image description here

在此处输入图片说明