【问题标题】:How to plot a linear regression with datetimes on the x-axis如何在 x 轴上绘制带有日期时间的线性回归
【发布时间】:2015-06-01 06:08:46
【问题描述】:

我的 DataFrame 对象看起来像

            amount
date    
2014-01-06  1
2014-01-07  1
2014-01-08  4
2014-01-09  1
2014-01-14  1

我想要一种散点图,其中 x 轴为时间,y 轴为数量,用一条线穿过数据来引导观察者的视线。如果我使用 pandas plot df.plot(style="o") 那就不太对了,因为这条线不存在。我想要类似 here 的例子。

【问题讨论】:

    标签: python pandas matplotlib time-series seaborn


    【解决方案1】:

    由于 Seaborn 在日期方面存在问题,我将创建一个解决方法。 首先,我将 Date 列设为我的索引:

    # Make dataframe
    df = pd.DataFrame({'amount' : [1,
                                   1,
                                   4,
                                   1,
                                   1]},
                      index = ['2014-01-06',
                               '2014-01-07',
                               '2014-01-08',
                               '2014-01-09',
                               '2014-01-14'])
    

    其次,将索引转换为pd.DatetimeIndex:

    # Make index pd.DatetimeIndex
    df.index = pd.DatetimeIndex(df.index)
    

    并用它替换原来的:

    # Make new index
    idx = pd.date_range(df.index.min(), df.index.max())
    

    第三,用新索引(idx)重新索引:

    # Replace original index with idx
    df = df.reindex(index = idx)
    

    这将为您没有数据的日期生成一个带有 NaN 值的新数据框:

    第四,由于 Seaborn 不能很好地处理日期和回归线,我将创建一个行数列,我们可以将其用作 x 轴:

    # Insert row count
    df.insert(df.shape[1],
              'row_count',
              df.index.value_counts().sort_index().cumsum())
    

    第五,我们现在应该能够使用“row_count”作为我们的 x 变量和“amount”作为我们的 y 变量来绘制回归线:

    # Plot regression using Seaborn
    fig = sns.regplot(data = df, x = 'row_count', y = 'amount')
    

    第六,如果您希望日期沿 x 轴而不是 row_count,您可以将 x-tick 标签设置为索引:

    # Change x-ticks to dates
    labels = [item.get_text() for item in fig.get_xticklabels()]
    
    # Set labels for 1:10 because labels has 11 elements (0 is the left edge, 11 is the right
    # edge) but our data only has 9 elements
    labels[1:10] = df.index.date
    
    # Set x-tick labels
    fig.set_xticklabels(labels)
    
    # Rotate the labels so you can read them
    plt.xticks(rotation = 45)
    
    # Change x-axis title
    plt.xlabel('date')
    
    plt.show();
    

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      注意:这与 Ian Thompson 的答案有很多共同之处,但方法不同,足以让它成为一个单独的答案。我使用问题中提供的 DataFrame 格式并避免更改索引。

      Seaborn 和其他库对日期时间轴的处理不如您希望的那样好。以下是我的解决方法:

      首先添加一列日期序数

      Seaborn 会比日期更好地处理这些问题。这是对日期和不喜欢日期的库进行各种数学运算的便捷技巧。

      from datetime import date
      
      df['date_ordinal'] = pd.to_datetime(df['date']).apply(lambda date: date.toordinal())
      

      用日期轴上的序数绘制图

      ax = seaborn.regplot(
          data=df,
          x='date_ordinal',
          y='amount',
      )
      # Tighten up the axes for prettiness
      ax.set_xlim(df['date_ordinal'].min() - 1, df['date_ordinal'].max() + 1)
      ax.set_ylim(0, df['amount'].max() + 1)
      

      用漂亮、可读的日期替换序数 X 轴标签

      ax.set_xlabel('date')
      new_labels = [date.fromordinal(int(item)) for item in ax.get_xticks()]
      ax.set_xticklabels(new_labels)
      

      哒哒!

      【讨论】:

      • 这太棒了!我只想补充一点,我必须使用new_labels = [dt.date.fromordinal(int(item)) for item in ax.get_xticks()],因为我的脚本顶部有import datetime as dt。我猜这个答案假设用户已经完成了from datetime import date
      【解决方案3】:
      • datetime dtype 值必须转换为类似ordinal
      • 这可以通过使用sklearn.linear_model.LinearRegression 计算模型然后使用matplotlib.pyplot.plot 添加回归线来完成
        • sns.lineplot(x=[x1_date, x2_date], y=[y1, y2], label='Linear Model', color='magenta') 也可以。
      • python 3.8.11pandas 1.3.2matplotlib 3.4.3sklearn 0.24.2中测试
      import yfinance as yf  # for data
      from sklearn.linear_model import LinearRegression
      import matplotlib.pyplot as plt
      
      # download the data
      data = yf.download('aapl', '2019-01-02', '2021-01-01')
      
      # add an ordinal column because sklearn doesn't work with datetimes
      data['ordinal'] = data.index.map(pd.Timestamp.toordinal)
      
      # create the model
      model = LinearRegression()
      
      # extract x and y from dataframe data
      x = data[['ordinal']]
      y = data[['Adj Close']]
      
      # fit the mode
      model.fit(x, y)
      
      # print the slope and intercept if desired
      print('intercept:', model.intercept_[0])
      print('slope:', model.coef_[0][0])
      
      # select x1 and x2 and get the corresponding date from the index
      x1 = data.ordinal.min()
      x1_date = data[data.ordinal.eq(x1)].index[0]
      x2 = data.ordinal.max()
      x2_date = data[data.ordinal.eq(x2)].index[0]
      
      # calculate y1, given x1
      y1 = model.predict(np.array([[x1]]))[0][0]
      
      print('y1:', y1)
      
      # calculate y2, given x2
      y2 = model.predict(np.array([[x2]]))[0][0]
      
      print('y2:', y2)
      
      [out]:
      intercept: -90078.45713565295
      slope: 0.12225139598567565
      y1: 28.279040945126326
      y2: 117.40030861868581
      

      情节

      ax1 = data.plot(y='Adj Close', c='k', figsize=(15, 6), grid=True, legend=False)
      ax1.plot([x1_date, x2_date], [y1, y2], label='Linear Model', c='magenta')
      ax1.legend()
      

      【讨论】:

        猜你喜欢
        • 2016-06-24
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        • 1970-01-01
        • 2018-01-17
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        相关资源
        最近更新 更多