熊猫:使用索引和值的日期时间索引遍历Series

时间:2019-08-31 15:04:49

标签: python pandas

我需要生成一个表示月度利润的数组,如下所示:

[
    [2008, None, None, None, 100, 100, 100, 100, 100, 100, 100, 100, 100],
    [2009, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
    # ecc...
    [2019, 100, 100, 100, 100, 100, 100, 100, 100, None, None, None, None, None]
]

换句话说:year, sum of profits for month 1, sum of profits for month 2, eccNone,该月没有信息。

从这样的每日利润数据框中

date
2008-04-01    0.0
2008-04-02    10.0
2008-04-03    10.0
2008-04-04    10.0
2008-04-05    10.0
Name: profit, dtype: float64

我打电话给df["profit"].groupby(pd.Grouper(freq='M')).sum()并得到:

date
2008-04-30    100.0
2008-05-31    100.0
2008-06-30    100.0
2008-07-31    100.0
2008-08-31    100.0
Freq: M, Name: profit, dtype: float64

现在,我正在考虑使用无效的伪代码执行以下操作:

start = df["date"].min().to_pydatetime()
end = df["date"].max().to_pydatetime()

result = [
    [start.year]
]
idx = 0
for date, monthly_profit in df["profit"].groupby(pd.Grouper(freq='M')).sum().iterrows():
    if date.year !== result[idx][0]:
        idx += 1
        result[idx] = [date.year]

    month = 1
    while month <= 12:
        if date.month == month:
            result[idx].append(monthly_profit)
        else:
            result[idx].append(None)
        month += 1

有没有要做的事情而无需迭代?如果没有,我该如何迭代和读取日期值?


编辑,以响应QuickBeam的回答,这是我的解决方案,当原始数据中没有全部12个月时,可以避免该问题:

if len(df.groupby([df.date.dt.month]).agg({"date": "count"})) < 12:
    # All months should be represented by at least one data point for the display to work properly
    # If not all months are present, we insert empty data
    min_year = df["date"].min().to_pydatetime().year
    for m in range(1, 13):
        if df.loc[df.date.dt.month == m].empty:
            df = df.append(pd.DataFrame({"date": datetime(min_year, m, 1), column_name: [np.nan]}))
        else:
            min_year = df.loc[df.date.dt.month == m]["date"].iloc[0].to_pydatetime().year

1 个答案:

答案 0 :(得分:2)

更多数据会很好,但我认为,您可以执行以下操作:

df.groupby([df.date.dt.year, df.date.dt.month])["profit"].sum()

然后,您可以轻松地使用枢轴获得所需的形状。给我一点时间,我将生成一些数据:)


好的,所以我认为我有一个不错的解决方案,但是您不需要数据透视。

import pandas as pd
import numpy as np
date_index = pd.date_range(start="2017-05-05", periods=700)
df = pd.DataFrame(data=np.random.rand(700), index=date_index, columns=["profit"])

现在提供所需的输出(但作为数据框,而不是列表列表)

df.groupby([df.index.year, df.index.month]).agg({"profit":"sum"}).unstack(-1)

给予

         profit                                                         \
             1          2          3          4          5          6    
2017        NaN        NaN        NaN        NaN  13.671041  16.693129   
2018  16.780003  12.783907  17.340193  13.323846  16.897318  16.671774   
2019  13.718783  14.322513  15.163668   1.606801        NaN        NaN   


             7          8          9          10         11         12  
2017  15.781419  15.357254  16.392586  13.782561  15.242144  15.897317  
2018  15.854918  17.360759  11.516470  17.096427  15.096696  16.593045  
2019        NaN        NaN        NaN        NaN        NaN        NaN 

所以您根本不需要枢轴。

注意但是,如果您的数据在某个时候不覆盖一年中的每个月,那么您将不会获得12列。但是您总是可以附加nan数据,这样一年中的每个月至少可以看到一次:)


因此,现在假设我们不涵盖所有月份,如以下示例所示:

date_index = pd.date_range(start="2017-05-05", periods=100)
df = pd.DataFrame(data=np.random.rand(100), index=date_index, columns=["profit"])
df = df.groupby([df.index.year, df.index.month]).agg({"profit":"sum"}).unstack(-1)

让我们研究一下列对象:

df.columns
MultiIndex([('profit', 5),
            ('profit', 6),
            ('profit', 7),
            ('profit', 8)],
           )

因此,我们不仅有一个列表,而且还有一个MultiIndex。 接下来,让我们定义想要的列标签(作为MultiIndex):

requird_columns_multiindex = pd.MultiIndex.from_tuples([("profit", month) for month in range(1,13)])

最后,我们将数据框df连接到一个仅包含列信息的空数据框:

pd.concat([df, pd.DataFrame(columns=requird_columns_multiindex)])
     profit                                                                 \
         1    2    3    4          5          6          7         8    9    
2017    NaN  NaN  NaN  NaN  12.733439  13.965117  14.504708  5.650205  NaN   


       10   11   12  
2017  NaN  NaN  NaN