首页 > 解决方案 > groupby 多索引生成 excel 数据透视表结果,解决方案太难看

问题描述

我正在分析带有日期和温度的气候数据,1950-2000 之间的每一天都有一个平均温度。我的目的是计算 1950-2000 年 50 年中最热的 10 个年份,并可视化在 1950-2000 年的 5 个十年中分别有多少年。我认为,具有多索引(0 级:十年,1 级:年)的 groupby 将是正确的方法。我曾期望返回 50 年(df 的长度),其中 5 个十年作为相应的 0 级索引。但相反,我得到 250 行(5 个十年 x 50 年)。这不是我想要的,也不是我需要的,因为分组数据框的十年 1950 包括所有其他十年的年份,但温度为零,其他几十年也一样 为什么 groupby 会这样做,我怎样才能获得 50 年的长度?我可以在 excel 中创建它,以几十年和几年为行,以平均温度为值: 在此处输入图像描述

希望你明白我的意思,下面是一个示例代码,我希望有助于重现问题。

一个(坏的)解决方案是从结果系列中删除所有值为零的行,但是如果一行的平均值实际上是 0,这既不优雅也不好:grouped = grouped[grouped != 0]

谢谢!

# import libraries
import pandas as pd
import numpy as np
import datetime
from numpy.random import randint
# create dataframe with dates as index and temperature on each date as column
cols = ['temperature']
dates = np.arange('1950', '2000', dtype='datetime64[D]')
len_dates = len(dates)
temp = randint(40, size=len_dates)
df = pd.DataFrame(temp, index=dates, columns=cols)

# extract year
df['year'] = df.index.year.astype('category')

# bin years into decades
cut_bins = np.arange(1950,2010,10).tolist()
cut_labels = [year for year in range(1950, 2000,10)]

df['decade'] = pd.cut(df['year'], bins=cut_bins, labels=cut_labels, include_lowest=True).astype('category')
df.head()

# groupby
grouped = df.groupby(['decade','year'])['temperature'].mean()
print('len(df): ', len(df))
print('len(grouped): ', len(grouped))
print('len(decade): ', len(df['decade'].unique()))
print('len(years): ', len(df['year'].unique()))
# resulting object has 250 rows yet only 50 years
print(grouped)

# remove needless rows from series
print(grouped = grouped[grouped != 0])

df.to_excel('groupedtest.xlsx')

标签: pythonpivotpandas-groupbymulti-index

解决方案


好吧,您要求 pandas 对包括十年/年在内的所有可能组求和,因此 pandas 将应用所有可能性的笛卡尔积(5 x 50 = 250)。

我相信您上面所做的大部分是正确的,唯一缺少的步骤就是对输出进行排序。这是我对这项任务的看法。

import pandas as pd
import numpy as np
import datetime
from numpy.random import randint
# create dataframe with dates as index and temperature on each date as column
cols = ['temperature']
dates = np.arange('1950', '2000', dtype='datetime64[D]')
len_dates = len(dates)
temp = randint(40, size=len_dates)
df = pd.DataFrame(temp, index=dates, columns=cols)

# extract year
df['year'] = df.index.year

idx = (
    df
    .groupby(["year"], as_index = False)  # group just by year
    .sum()  # find temp for the year, which from the question should be the sum
    .assign(
        decade = lambda df: df["year"].mod(100) - df["year"].mod(10)
    ) # compute the decade after grouping
    .sort_values("temperature", ascending = False)  # sort by highest temp
    .set_index("decade")
    .head(10) # get top 10
    .index
)

获得前 10 名后,您可以使用计数器。

from collections import Counter
Counter(idx)

推荐阅读