如何用Python实现按区域和年份循环计算NDVI绿化进度差?
问题描述
我拥有16个区域的NDVI数据,需找出各区域每年绿化面积达5%和95%对应的日序(doy),计算两者差值。此前通过手动重复代码完成,现希望用Python循环实现该流程,将所有区域、年份的结果整合为一个大型DataFrame。
手动处理的代码片段如下:
import pandas as pd import numpy as np x = pd.read_csv('D:/data.csv') x = x[x['means'] > 0] x = x[x['diff'] > -1.5] x = x[x['area'] > 9318] x = x.sort_values(by = 'doy') AKB = x[x['name'] == 'Region1'].drop_duplicates(subset=['ID', 'Year'], keep = 'first') AKB['cummulative_area'] = AKB.groupby(['Year'])['area'].cumsum() AKT = x[x['name'] == 'Region2'].drop_duplicates(subset=['ID', 'Year'], keep = 'first') AKT['cummulative_area'] = AKT.groupby(['Year'])['area'].cumsum() # 找出5%和95%绿化面积对应的日序并计算差值 AKB01 = AKB[AKB['Year'] == 2001] AKB01fifth = AKB01[AKB01['cumulative_area'] > AKB01['area'].sum() * 0.05] AKB01ninefifth = AKB01[AKB01['cumulative_area'] > AKB01['area'].sum() * 0.95] AKB01 = AKB01ninefifth.doy.min() - AKB01fifth.doy.min() AKB02 = AKB[AKB['Year'] == 2002] AKB02fifth = AKB02[AKB02['cumulative_area'] > AKB02['area'].sum() * 0.05] AKB02ninefifth = AKB02[AKB02['cumulative_area'] > AKB02['area'].sum() * 0.95] AKB02 = AKB02ninefifth.doy.min() - AKB02fifth.doy.min() # ... 后续年份重复代码省略 AKB19 = AKB[AKB['Year'] == 2019] AKB19fifth = AKB19[AKB19['cumulative_area'] > AKB19['area'].sum() * 0.05] AKB19ninefifth = AKB19[AKB19['cumulative_area'] > AKB19['area'].sum() * 0.95] AKB19 = AKB19ninefifth.doy.min() - AKB19fifth.doy.min() # ... 其他区域重复代码省略 AKBign = pd.DataFrame() AKBign['year'] = np.arange(2001,2020,1) AKBign['difference'] = [AKB01,AKB02,AKB03,AKB04,AKB05,AKB06,AKB07,AKB08,AKB09,AKB10,AKB11,AKB12,AKB13,AKB14,AKB15,AKB16,AKB17,AKB18,AKB19]
循环实现方案
以下是用Pandas分组和自定义函数实现的自动化流程,无需手动重复代码:
import pandas as pd import numpy as np # 1. 数据预处理 x = pd.read_csv('D:/data.csv') # 合并过滤条件,减少重复操作 x = x[(x['means'] > 0) & (x['diff'] > -1.5) & (x['area'] > 9318)] # 按日序排序 x = x.sort_values(by='doy') # 去重:每个ID和Year保留第一条记录 x = x.drop_duplicates(subset=['ID', 'Year'], keep='first') # 2. 计算各区域每年的累计绿化面积 x['cumulative_area'] = x.groupby(['name', 'Year'])['area'].cumsum() # 3. 自定义函数:计算单一年份的5%/95%日序及差值 def calculate_season_metrics(group): total_area = group['area'].sum() # 找到累计面积达5%的最小日序 doy_5p = group[group['cumulative_area'] >= total_area * 0.05]['doy'].min() # 找到累计面积达95%的最小日序 doy_95p = group[group['cumulative_area'] >= total_area * 0.95]['doy'].min() # 返回结果,缺失值自动填充为NaN return pd.Series({ 'doy_5p': doy_5p, 'doy_95p': doy_95p, 'difference': doy_95p - doy_5p }) # 4. 按区域和年份分组批量计算 result = x.groupby(['name', 'Year']).apply(calculate_season_metrics).reset_index() # 5. 整理结果列顺序(可选) result = result[['name', 'Year', 'doy_5p', 'doy_95p', 'difference']] # 可选:筛选特定年份范围(比如2001-2019) result = result[result['Year'].between(2001, 2019)]
代码说明
- 预处理优化:合并过滤条件,一次性完成去重和排序,避免重复编写过滤逻辑。
- 批量累计计算:直接按
name和Year分组计算累计面积,无需单独处理每个区域。 - 函数封装逻辑:将单一年份的计算逻辑封装成函数,通过
groupby.apply自动遍历所有区域和年份,彻底替代手动重复代码。 - 结果整合:最终
result是包含所有区域、年份的完整DataFrame,清晰展示每个区域每年的5%日序、95%日序及两者差值。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

