divingTobi
divingTobi

Reputation: 2300

Dual y-axis plot using seaborn objects (v0.12)

I am trying to make a dual y-axis plot using the new seaborn interface (seaborn.objects, available in v0.12). However, I am having difficulties getting it to work.

My first try is this:

import seaborn.objects as so

df = pd.DataFrame(
    {"x": [1, 2, 3, 4, 5], "y1": [5, 2, 1, 6, 2], "y2": [100, 240, 130, 570, 120]}
)
fig, ax1 = plt.subplots(1, 1, figsize=(10, 5))
ax2 = ax1.twinx()
so.Plot(df, x="x").add(so.Bar(), y="y1", ax=ax1).add(so.Line(), y="y2", ax=ax2)

But this will create the seaborn plot with one y-axis and an empty dual-axis plot.

Upvotes: 0

Views: 849

Answers (3)

divingTobi
divingTobi

Reputation: 2300

Here is the solution I finally came up with:

import seaborn.objects as so
import pandas as pd
import matplotlib.pyplot as plt


df = pd.DataFrame(
    {"x": [1, 2, 3, 4, 5], "y1": [5, 2, 1, 6, 2], "y2": [1000, 240, 1300, 570, 120]}
)
fig, ax1 = plt.subplots(1, 1, figsize=(6, 4))
ax2 = ax1.twinx()
ax1.tick_params(axis="x", labelrotation=45)
p1 = (
    so.Plot(df, x="x", y="y1")
    .add(so.Bar(width=0.7))
    .label(x="Month", y="Data 1", title="TITLE")
    .on(ax1)
    .plot()
)
p2 = (
    so.Plot(df, x="x", y="y2")
    .add(so.Line(color="orange", linewidth=3))
    .label(y="Data 2", title="TITLE")
    .scale(y=so.Continuous().label(like="{x:,.0f}"))
    .on(ax2)
    .plot()
)

The result is pretty much what I was looking for. enter image description here

Now here is an interesting fact. The above image was generated with python 3.10.11 and seaborn 0.12.2.

When switching to python 3.11.4 and seaborn 0.13.2, I get the following (note the y-axis tick labels on the left:

enter image description here

This seems to be a bug in seaborn.

Upvotes: 0

itthrill
itthrill

Reputation: 1376

How can we refine this?

h= sns.load_dataset("healthexp")
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
ax.clear()
plt.close()
ax2= ax.twinx()
p=(
so.Plot(h, x="Year")
)

p.add(so.Bar(),so.Agg('sum'), y="Spending_USD").on(ax).plot()
p=p.add(so.Line(),so.Agg(), y="Life_Expectancy").on(ax2)
p=p.label(
             x="", 
             y="",
             title="Spending",
        )
p

enter image description here

Even the output of original plot has messed up axis label.

df = pd.DataFrame(
    {"x": [1, 2, 3, 4, 5], "y1": [5, 2, 1, 6, 2], "y2": [100, 240, 130, 570, 120]}
)
fig, ax1 = plt.subplots(1, 1, figsize=(10, 5))
ax2 = ax1.twinx()
p = so.Plot(df, x="x")
p.add(so.Bar(), y="y1").on(ax1).plot()
p.add(so.Line(), y="y2").on(ax2).plot()

enter image description here

Upvotes: 1

mwaskom
mwaskom

Reputation: 49032

You'll want to call Plot.on to use pre-existing matplotlib axes:

import seaborn.objects as so

df = pd.DataFrame(
    {"x": [1, 2, 3, 4, 5], "y1": [5, 2, 1, 6, 2], "y2": [100, 240, 130, 570, 120]}
)
fig, ax1 = plt.subplots(1, 1, figsize=(10, 5))
ax2 = ax1.twinx()
p = so.Plot(df, x="x")
p.add(so.Bar(), y="y1").on(ax1).plot()
p.add(so.Line(), y="y2").on(ax2).plot()

enter image description here

Note that there will likely be a Plot.twin method to make this more natural, but it's not been implemented yet.

Upvotes: 1

Related Questions