我需要生成一个表示月度利润的数组,如下所示:
[
[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, ecc
和None
,该月没有信息。
从这样的每日利润数据框中
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
答案 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