如何在侧面有差异的不同子图中绘制对

how to plot pairs in different subplots with difference on the side

我想在 seaborn 中进行绘图,但遇到了一些困难。数据有 2 个变量:时间(2 个级别)和状态(2 个级别)。我想在 x 轴上绘制时间并将状态绘制为不同的子图,显示单独的数据线。最后,在这些右侧,我想为每个状态级别显示时间 2 和时间 1 之间差异的差异图。我不能做得很好,因为我无法让第二个情节显示在右边。这是我的尝试:

import numpy as np
import pandas as pd
import seaborn as sns

# Just making some fake data
ids = [1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5]
times = [1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,2]
states = ['A', 'B', 'A', 'B'] * 5

np.random.seed(121)
resps = [(i*t) + np.random.normal() for i, t in zip(ids, times)]

DATA = {
    'identity': ids,
    'time': times,
    'state': states,
    'resps': resps
}
df = pd.DataFrame(DATA)
# Done with data

g = sns.relplot(
    data=df, kind='line',
    col='state', x='time', y='resps', units='identity',
    estimator=None, alpha=.5, height=5, aspect=.7)

# # Draw a line onto each Axes
g.map(sns.lineplot,"time", "resps", lw=5, ci=None)

# Make a wide data to make the difference
wide = df.set_index(['identity', 'state', 'time']).unstack().reset_index()

A = wide['state']=='A'
B = wide['state']=='B'

wide['diffA'] = wide[A][('resps', 2)] - wide[A][('resps', 1)]
wide['diffB'] = wide[B][('resps', 2)] - wide[B][('resps', 1)]

wide['difference'] = wide[['diffA', 'diffB']].sum(axis=1)
wide = wide.drop(columns=[('diffA', ''), ('diffB', '')])

sns.pointplot(x='state', y='difference', data=wide, join=False)

第一个的输出

第二个输出:

有没有办法把它们放在一起?即使它们是不同的数据?我确实尝试使用 matplotlib。然后取得了稍微好一点的结果,但这仍然有问题,因为我希望左边的两个图有一个共享的 y 轴,但没有差异。这也带来了很多工作,因为我想灵活地处理不同数量的 state 变量,但为了简单起见只保持为 2。这是我想要做的油漆版本(抱歉质量差),希望对外观有更多控制,但这是次要的:

有没有更简单可靠的方法来做到这一点?谢谢!

问题是 sns.relplot 在图形级别运行。这意味着它创建了自己的图形对象,我们无法控制它使用的轴。如果您想在不使用“纯”matplotlib 的情况下利用 seaborn 创建线条,您可以复制 matplotlib 轴上的线条:

import numpy as np
import pandas as pd
import seaborn as sns

# Just making some fake data
ids = [1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5]
times = [1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,2]
states = ['A', 'B', 'A', 'B'] * 5

np.random.seed(121)
resps = [(i*t) + np.random.normal() for i, t in zip(ids, times)]

DATA = {
    'identity': ids,
    'time': times,
    'state': states,
    'resps': resps
}
df = pd.DataFrame(DATA)
# Done with data

g = sns.relplot(
    data=df, kind='line',
    col='state', x='time', y='resps', units='identity',
    estimator=None, alpha=.5, height=5, aspect=.7)

# # Draw a line onto each Axes
g.map(sns.lineplot,"time", "resps", lw=5, ci=None)

# Make a wide data to make the difference
wide = df.set_index(['identity', 'state', 'time']).unstack().reset_index()

A = wide['state']=='A'
B = wide['state']=='B'

wide['diffA'] = wide[A][('resps', 2)] - wide[A][('resps', 1)]
wide['diffB'] = wide[B][('resps', 2)] - wide[B][('resps', 1)]

wide['difference'] = wide[['diffA', 'diffB']].sum(axis=1)
wide = wide.drop(columns=[('diffA', ''), ('diffB', '')])

# New code ----------------------------------------
import matplotlib.pyplot as plt
plt.close(g.figure)

fig = plt.figure(figsize=(12, 4))

ax1 = fig.add_subplot(1, 3, 1)
ax2 = fig.add_subplot(1, 3, 2, sharey=ax1)
ax3 = fig.add_subplot(1, 3, 3)

l = list(g.axes[0][0].get_lines())
l2 = list(g.axes[0][1].get_lines())

for ax, g_ax in zip([ax1, ax2], g.axes[0]):
    l = list(g_ax.get_lines())
    for line in l:
        ax.plot(line.get_data()[0], line.get_data()[1], color=line.get_color(), lw=line.get_linewidth())
    ax.set_title(g_ax.get_title())

sns.pointplot(ax=ax3, x='state', y='difference', data=wide, join=False)
# End of new code ---------------------------------- 

plt.show()

结果: