【问题标题】:Plot multiple variables on same plot, and panel plot by station ID在同一图上绘制多个变量,并按站 ID 绘制面板图
【发布时间】:2022-02-06 01:30:18
【问题描述】:

我有 2 个时间序列数据帧,它们来自两个二维数组。这些数据帧的结构是:

生成示例数据帧

import pandas as pd
import numpy as np
date_range = pd.period_range('1981-01-01','1981-01-04',freq='D')
x = np.arange(8).reshape((4,2))
y = np.arange(8).reshape((4,2))
x = pd.DataFrame(x, index = date_range, columns = ['station1','station2'])
y = pd.DataFrame(y, index = date_range, columns = ['station1','station2'])
print(x)
          station1 station2
1981-01-01  0        1
1981-01-02  2        3
1981-01-03  4        5
1981-01-04  6        7

目标

我想生成一个多图,其中 'x' 和 'y' 的值在同一个图上绘制为线,x 和 y 按颜色分割,但每个站有多个图的“行” .使用上面的示例代码,每个单独的图表将绘制不同的站列。

我尝试过的

我尝试了 seaborn 路线:首先将两个数据帧连接在一起 - 每个 df 代表一个变量,因此我将它们添加为键以在连接后命名这些变量。然后我使用了 melt 来对它们进行多重绘图:

df = pd.concat([x , y], keys = ['Var1', 'Var2'])
meltdf = df.melt(var_name = 'Station', value_name = 'Value', ignore_index = False)
print(meltdf)
                  Station  Value
Var1 1981-01-01  station1      0
     1981-01-02  station1      2
     1981-01-03  station1      4
     1981-01-04  station1      6
Var2 1981-01-01  station1      0
     1981-01-02  station1      2
     1981-01-03  station1      4
     1981-01-04  station1      6
Var1 1981-01-01  station2      1
     1981-01-02  station2      3
     1981-01-03  station2      5
     1981-01-04  station2      7
Var2 1981-01-01  station2      1
     1981-01-02  station2      3
     1981-01-03  station2      5
     1981-01-04  station2      7

我想将 Var1 和 Var2 的值绘制为 station1 的同一图表上的线,station2 的相同,依此类推。我想保留日期作为索引,因为这些应该是时间序列图,“日期”沿 x 轴。我试过这个 non-working 代码(例如):

import seaborn as sns
sns.relplot(data=df, x = 'Var1', y = 'Var2', kind = 'line', hue = 'keys', row = 'Station')

我应该“双重融化” dfs 以将变量类型作为自己的 col 吗? concat + keys 步骤似乎不正确。

【问题讨论】:

    标签: python pandas matplotlib seaborn


    【解决方案1】:

    pd.concatpd.melt,你在正确的轨道上,其次是 seaborn relplot。我会这样处理它:

    import pandas as pd
    import seaborn as sns
    from matplotlib import pyplot as plt
    
    #data generation
    import numpy as np
    np.random.seed(123)
    date_range = pd.period_range('1981-01-01','1981-01-04',freq='D')
    x = np.random.randint(1, 10, (4,2))
    y = np.random.randint(1, 10, (4,2))
    x = pd.DataFrame(x, index = date_range, columns = ['station1','station2'])
    y = pd.DataFrame(y, index = date_range + pd.to_timedelta(1, unit="D"), columns = ['station1','station2'])
    
    #keep information where each data point comes from
    x["key"], y["key"] = "x", "y"
    
    #combining dataframes and reshaping 
    df = pd.concat([x, y]).melt(["key"], var_name="station", value_name="station_value",  ignore_index = False)
    
    #plotting - the datetime conversion might not be necessary 
    #depending on the datetime format of your original dataframes
    #best approach is conversion to datetime index when creating the dataframes
    fg = sns.relplot(data=df, x = pd.to_datetime(df.index.to_timestamp()), y = "station_value", kind = "line", hue = "key", row = "station")
    
    #shouldn't be necessary but this example had too many ticks for the interval
    from matplotlib.dates import DateFormatter, DayLocator
    fg.axes[0,0].xaxis.set_major_locator(DayLocator(interval=1))
    fg.axes[0,0].xaxis.set_major_formatter(DateFormatter("%y-%m-%d"))
    
    plt.show()
    

    示例输出:

    如果pandas版本不能处理重复的索引条目,我们可以重写为:

    import pandas as pd
    import seaborn as sns
    from matplotlib import pyplot as plt
    
    #data generation
    import numpy as np
    np.random.seed(123)
    date_range = pd.period_range('1981-01-01','1981-01-04',freq='D')
    x = np.random.randint(1, 10, (4,2))
    y = np.random.randint(1, 10, (4,2))
    x = pd.DataFrame(x, index = date_range, columns = ['station1','station2'])
    y = pd.DataFrame(y, index = date_range + pd.to_timedelta(1, unit="D"), columns = ['station1','station2'])
    
    #keep information where each data point comes from
    x["key"], y["key"] = "x", "y"
    #moving index into a column 
    x = x.reset_index()
    y = y.reset_index()
    #and changing it to datetime values that seaborn can understand
    #only necessary because your example contains pd.Period data
    x["index"] = pd.to_datetime(x["index"].astype(str))
    y["index"] = pd.to_datetime(y["index"].astype(str))
    
    #combining dataframes and reshaping 
    df = pd.concat([x, y]).melt(["index", "key"], var_name="station", value_name="station_value")
    
    #plotting
    fg = sns.relplot(data=df, x = "index", y = "station_value", kind = "line", hue = "key", row = "station")
    
    #shouldn't be necessary but this example had too many ticks for the interval
    from matplotlib.dates import DateFormatter, DayLocator
    fg.axes[0,0].xaxis.set_major_locator(DayLocator(interval=1))
    fg.axes[0,0].xaxis.set_major_formatter(DateFormatter("%y-%m-%d"))
    
    plt.show()
    

    【讨论】:

    • 这很奇怪 - 我得到一个 ValueError: cannot reindex from a duplicate axis 尝试这种方式
    • 使用示例代码还是使用您的真实数据?我认为df = pd.concat([x, y]) 是这里的问题。回溯是否提供有关潜在问题的更多信息?这里:matplotlib 3.5.1、seaborn 0.11.0、NumPy 1.22.1、pandas 1.3.5
    • 对我来说都是示例数据和真实数据。来自 Pandas 的许多“重新索引”错误。我正在尝试寻找不同的方法来添加日期索引,因为this 使我看起来好像有一个包含重复值的索引。 seaborn 0.11.2 熊猫 1.3.3
    • 是的,索引包含重复值。我知道在过去这给熊猫带来了问题。不太可能,尽管他们从 1.3.3 到 1.3.5 解决了这个问题。好吧,那么我们必须将索引移动到列中。
    • Pandas 版本很重要! 有趣的是 - 这并没有绘制,所以我从 1.3.3 升级到 1.4.0 并绘制了。非常感谢@Mr。 T - 索引重置似乎很复杂,但奇怪的用例表明了这一点。
    猜你喜欢
    • 2020-07-07
    • 2021-07-31
    • 1970-01-01
    • 2021-07-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多