用Python xarray减去每个网格的平均月份值的最佳方法

4

这里提供了一个玩具数据集,源自此处

import numpy as np
import pandas as pd
import seaborn as sns

import xarray as xr

np.random.seed(123)

xr.set_options(display_style="html")

times = pd.date_range("2000-01-01", "2001-12-31", name="time")
annual_cycle = np.sin(2 * np.pi * (times.dayofyear.values / 365.25 - 0.28))

base = 10 + 15 * annual_cycle.reshape(-1, 1)
tmin_values = base + 3 * np.random.randn(annual_cycle.size, 3)
tmax_values = base + 10 + 3 * np.random.randn(annual_cycle.size, 3)

ds = xr.Dataset(
    {
        "tmin": (("time", "location"), tmin_values),
        "tmax": (("time", "location"), tmax_values),
    },
    {"time": times, "location": ["IA", "IN", "IL"]},
)

我知道通过这里,可以找到如何从xarray.DataSet()的变量中减去月平均值的方法,如下所示:

climatology = ds.groupby("time.month").mean("time")
anomalies = ds.groupby("time.month") - climatology
anomalies.mean("location").to_dataframe()[["tmin", "tmax"]].plot()

那么,我可以为每个位置进行减法吗?

我尝试对按位置和月份分组的数据进行减法,但是 xarray.DataSet.groupby() 不允许传递多个分组。 然后,我尝试使用 xarray.DataSet.stack() 创建位置-月份数据,但它只允许传递维度;我可以使用 time.month 提取月份值,但它们被恢复为一个新变量,而不是一个维度。 我可以使用 forxarray.DataSet.apply() 处理所有位置,但速度太慢了(我有大约65000个位置)。

期望的结果或过程类似于:

for each location:
    climatology = ds.groupby("time.month").mean("time")
    anomalies = ds.groupby("time.month") - climatology

最好的解决方案是仅使用xarray,但如果使用pd.DataFrame()或其他方法也可以实现快速解决,则这些解决方案也受欢迎。

编辑

以下是我当前使用`pd.DataFrame()`的解决方案:

# convert to pd.dataframe
df = ds.to_dataframe()

# get mean monthly values
months = df.index.get_level_values('time').month
df_meanMonths = df.groupby([pd.Grouper(level='location'), months]).mean()

# rename and reindex
df_meanMonths.rename(columns={'tmin': 'tminMM', 'tmax': 'tmaxMM'}, inplace=True)
df_meanMonths.index.set_names('month', level='time', inplace=True)

# merge
df['month'] = df.index.get_level_values('time').month
vars_join = ['tminMM', 'tmaxMM']
join_right = df_meanMonths[vars_join]

# results
df.reset_index().set_index(['location', 'month']).merge(join_right, how='left', left_index=True, right_on=['location', 'month'])
1个回答

1
我认为你可能正在寻找的是这个:

我认为你可能需要的是这个:

anomalies = xr.apply_ufunc(
    lambda x, mean: x - mean, 
    ds.tmax.groupby('time.month'),
    ds.tmax.groupby('time.month').mean()
).drop('month')

仅针对tmax变量(DataArray)或

anomalies = xr.apply_ufunc(
    lambda x, means: x - means, 
    ds.groupby('time.month'),
    ds.groupby('time.month').mean()
).drop('month')

针对数据集中的所有变量


谢谢!您的代码实现过程看起来和我想象中的一样,但是我对结果不太确定。例如,我将在apply_ufunc()内计算的tmin的平均值与通过tmin数组计算的平均值(即np.mean(ds.tmin.values))进行比较,并发现它们并不完全相同(例如,对于2000年1月,-5.16275与-5.34013)。您能否详细说明一下这个问题? - hlee
嘿,你的评论不是很清楚。从我的理解来看,你试图将减法与月平均值进行比较:ds.tmin.values - anomalies 应该等于实际气候。我执行了 (ds.tmin - anomalies.tmin).sel(time='2000-01-01', location='IA')ds.isel(time=(ds.time.dt.month==1)).mean('time').sel(location='IA'),两者都给出了相同的结果(-5.16275)。 - Agustín Begue
嗨,你说得对。我在验证过程中犯了一个错误。谢谢! - hlee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接