【问题标题】:Python dataframe accumulation line plotPython数据框累积线图
【发布时间】:2017-11-27 14:44:37
【问题描述】:

我在 DataFrame 中有每日沉淀,df,看起来像:

   Jan   Feb   Mar   Apr   May   Jun   Jul   
0   0.00  0.00  0.07  0.02  0.00  0.00   NaN  
1   0.80   NaN  0.00  0.00  0.03  0.00  0.00  
2   0.20  0.00   NaN  0.14  0.00  0.00  0.00 
3   0.00  0.00  0.00  0.01  0.00  0.00  0.00  
4    NaN   NaN  0.00  0.00  0.90  0.50  0.00  
5   0.01  0.00  0.00  0.12  0.17   NaN  0.77  
6   0.77   NaN   NaN  0.00  0.18   NaN  0.00  
7   0.00   NaN  0.04  0.00  0.00  0.00  0.11  
8   0.00  0.56  0.00  0.00  0.02  0.00  0.00  
9   0.00  0.00  0.04  0.00  0.00  0.00  0.00  
10  0.16  0.00  0.00  0.00  0.42  0.00  0.00  
11  0.00  0.08  0.00  0.00  0.78  0.00  0.00  
...

一年中每个月的所有日子。我想将所有这些数据绘制到显示累积的单个运行折线图上(即,如果在第 1 天和第 3 天下雨,那么第 3 天绘制的点将是第 1+3 天的总降雨量,那么如果第 5 天下雨,情节将是第 1+3+5 天,依此类推)。像这样添加值和绘图的最佳方法是什么?

【问题讨论】:

    标签: python pandas dataframe matplotlib


    【解决方案1】:

    您似乎在寻找.stack()。但是,您的问题似乎比 .stack() + .cumsum() 稍微复杂一些。这是因为,假设您有一个 31x12 的 DataFrame(日行、月列),您需要告诉 pandas 如何将每个月/日组合映射到一年中的某一天;否则,熊猫会假设你有 372 天年。下面的第一个函数执行此操作,下面的行首先创建一些示例数据,然后使用该函数帮助获取按年计算的累积总和。

    import datetime
    
    
    def stack_daily(df, year='current'):
        """Construct pd.DatetimeIndex from unstacked day/month format."""
        # Confirm index is 1-indexed and ends at 31
        if not np.array_equal(df.index, pd.RangeIndex(1, 32)):
            raise ValueError('`df` should have `pd.RangeIndex(1, 32)`')
        # Same logic for columns
        if not np.array_equal(df.columns, pd.RangeIndex(1, 13)):
            raise ValueError('`df` should have columns `pd.RangeIndex(1, 13)`')
        if year == 'current':
            year = datetime.date.today().year
        stacked = df.stack()  # Implicit dropna=True
        day, month = zip(*stacked.index.get_values())
        dates = {'year': [year] * stacked.shape[0],
                 'month': month,
                 'day': day}
        return pd.to_datetime(dates)
    
    
    # Create random precipitation data
    import numpy as np
    import pandas as pd
    
    
    np.random.seed(123)
    data = np.empty((31, 12))
    data[:] = np.nan
    mask = np.random.randint(0, 2, size=data.shape, dtype=np.bool)
    vals = np.random.rand(*data.shape)
    data[mask] = vals[mask]
    data[29:] = np.nan
    df = pd.DataFrame(data, index=pd.RangeIndex(1, 32),
                      columns=pd.RangeIndex(1, 13))
    
    # Manipulate to get day-of-year index
    idx = stack_daily(df)
    doy = idx.dt.dayofyear
    
    total_precip = df.stack().reset_index(drop=True)
    total_precip.index = doy
    total_precip.dropna(inplace=True)
    total_precip = total_precip.sort_index().cumsum()
    
    # %matplotlib inline
    total_precip.plot()
    

    更新

    新功能:

    import datetime
    
    
    def stack_daily(df, year='current', sort=True, dropna=True):
        """Construct pd.DatetimeIndex from unstacked day/month format."""
        # Confirm index is 1-indexed and ends at 31
        if not np.array_equal(df.index, pd.RangeIndex(1, 32)):
            raise ValueError('`df` should have `pd.RangeIndex(1, 32)`')
        # Same logic for columns
        if not np.array_equal(df.columns, pd.RangeIndex(1, 13)):
            raise ValueError('`df` should have columns `pd.RangeIndex(1, 13)`')
        if year == 'current':
            year = datetime.date.today().year
        stacked = df.stack(dropna=False)
        year = np.repeat(year, stacked.shape[0])  # len == 372
        day, month = zip(*stacked.index.get_values())
    
        # Drop the *difference* between a valid calendar and the 372-day calendar
        #     created by using 12 31-day months.
        # Use a pure-Python solution here because NumPy set logic doesn't generally
        #     support 2d arrays and we have fairly small data (1 year).
        true_dates = pd.date_range(start='{}-01-01'.format(year[0]),
                                   end='{}-12-31'.format(year[0]))
        true_dates = list(zip(true_dates.day,
                              true_dates.month,
                              true_dates.year))
        full_dates = list(zip(day, month, year))
        # We want a boolean mask False where dates are invalid
        # This should yield len(mask[mask == 1]) == 365
        mask = np.array([date in true_dates for date in full_dates])
    
        # Now filter stacked data on this mask
        stacked = stacked.loc[mask]
    
        # And finally repeat above process converting to datetime
        #     and then getting day of year.
        day, month = zip(*stacked.index.get_values())
        dates = {'year': year[:stacked.shape[0]].tolist(),
                 'month': month,
                 'day': day}
        stacked.index = pd.to_datetime(dates).dt.dayofyear
    
        if sort:
            stacked.sort_index(inplace=True)
        if dropna:
            stacked.dropna(inplace=True)
        return stacked
    

    例子:

    # Create random precipitation data
    # This gets you a DataFrame with 12 months on the columns and
    #     31 days on the index.  Both are 1-indexed i.e. start at 1.
    #     There is a mix of 0.00s, NaN, and other floats, mimicking
    #     the data from your question.
    import numpy as np
    import pandas as pd
    
    np.random.seed(123)
    data = np.zeros((31, 12))
    mask1 = np.random.randint(0, 2, size=data.shape, dtype=np.bool)
    mask2 = np.random.randint(0, 2, size=data.shape, dtype=np.bool)
    vals = np.random.rand(*data.shape)
    nans = np.zeros(data.shape)
    nans[:] = np.nan
    data[mask1] = vals[mask1]
    data[mask2] = nans[mask2]
    df = pd.DataFrame(data, index=pd.RangeIndex(1, 32),
                      columns=pd.RangeIndex(1, 13))
    
    # %matplotlib inline
    stack_daily(df).cumsum().plot()
    

    【讨论】:

    • 我收到一条错误消息“RuntimeError:定位器尝试生成从 0.2 到 366.8 的 1468 个刻度:超过 Locator.MAXTICKS”。我以前从未见过这个错误。
    • 很遗憾没有。在确保我的数据框大小正确并应用您的代码后,我仍然收到错误“ValueError:无法组装日期时间:日期超出月份的范围”。将继续努力。
    • 编辑:我在闰年遇到了这个错误。我在非闰年尝试过,脚本运行良好!将尝试看看闰年发生了什么......谢谢!!!
    • 更新@JMP0629
    • 这很好用。有没有一种简单的方法可以在 x 轴上放置带有月份标签的月份刻度而不是儒略日?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    相关资源
    最近更新 更多