求助:为多站点数据框添加逐时间步P/PET/Q异常值列
问题排查与代码修正
原代码问题分析
- 最终通过
unlist(results)将嵌套列表转为长格式数据,完全偏离了“在原数据列后追加异常值列”的需求 - 循环生成的结果仅存储了异常值,未与原数据对应拼接,也未维护原数据的列顺序
修正方案(两种实现方式)
方式1:使用tidyverse的宽转长/长转宽流程(简洁高效)
利用pivot_longer将数据转为长格式,按站点和参数分组计算异常值,再转回宽格式得到目标结果:
library(tidyverse) library(lubridate) # 保留用户提供的原数据df df <- data.frame( stringsAsFactors = FALSE, check.names = FALSE, Date = c("01/11/1876","01/12/1876", "01/01/1877","01/02/1877","01/03/1877", "01/04/1877","01/05/1877","01/06/1877", "01/07/1877","01/08/1877","01/09/1877", "01/10/1877","01/11/1877","01/12/1877", "01/01/1878"), `Att-Bissen P [mm]` = c(48.5,111.2,29.7,139.4,90.1,25.9, 216,94.6,40.5,NA,64.4,68.8,44.7, 34.8,71.9), `Att-Bissen PET [mm]` = c(88.4,88.3,80.5,53.4,36.7,20.2, 21.6,21.7,21.3,37.6,46.1,66.5,89.8, 121.5,87.7), `Att-Bissen Q [mm]` = c(13.5,12.6,11.3,12.9,44.6,21.3, 194.9,NA,49.1,46.7,63.6,25.4,19.8, 15.3,16), `Rau. Merl P [mm]` = c(43.7,104.2,25.5,131.3,83.7,21.9, 205.2,88.1,35.9,61,59,63.2,40, 30.4,66.2), `Rau. Merl PET [mm]` = c(91.4,91.3,83.2,54.9,37.5,20.3, 21.8,21.8,21.4,38.4,47.3,68.6,NA, 125.9,90.7), `Rau. Merl Q [mm]` = c(8.7,10.6,8.4,14.3,23.7,14.1, 131.6,106.7,40.1,42.4,50.3,24.6,16.7, 11.3,13.7), `Syre Felsmuhle/Mertert P [mm]` = c(37.8,89.5,22.3,112.7,72,19.2, 175.8,75.8,31.2,52.6,50.9,54.5,34.7, 26.5,57.1), `Syre Felsmuhle/Mertert PET [mm]` = c(95.6,95.6,86.9,57.2,38.8,20.7, 22.3,22.3,21.9,39.8,49.2,71.6,97.2, 132,94.9), `Syre Felsmuhle/Mertert Q [mm]` = c(16,22,17.9,24,23.1,11.4,91,NA, NA,45.2,65.6,NA,NA,NA,NA), `Wiltz-Winseler P [mm]` = c(50.1,106.9,33,132.4,87.7,29.7, 201.8,91.8,42.8,66.4,64.5,68.5,46.7, 37.7,71.3), `Wiltz-Winseler PET [mm]` = c(87.4,87.3,79.5,52.5,35.8,19.4, 20.8,20.8,20.4,36.7,NA,NA,88.8, 120.4,86.7), `Wiltz-Winseler Q [mm]` = c(7.2,6.3,5,8.6,33.9,32.2,234.2, 148.1,68.5,51.5,101.4,25.7,18.7, 14.3,12.1)) # 宽转长,提取站点与参数信息 df_long <- df %>% pivot_longer(-Date, names_to = c("Site", "Parameter"), names_pattern = "(.*) (P|PET|Q) \\[mm\\]", values_to = "Value") # 分组计算异常值 df_anomaly <- df_long %>% group_by(Site, Parameter) %>% mutate(Anomaly = Value - mean(Value, na.rm = TRUE)) %>% ungroup() # 转回宽格式,调整列顺序使原参数列与异常值列相邻 df_final <- df_anomaly %>% pivot_wider(names_from = Parameter, values_from = c(Value, Anomaly), names_glue = "{Site} {.value} {Parameter} [mm]") %>% select(Date, starts_with("Att-Bissen"), starts_with("Rau. Merl"), starts_with("Syre Felsmuhle/Mertert"), starts_with("Wiltz-Winseler"))
方式2:修正原有循环逻辑
保留原循环结构,调整结果拼接方式,将异常值列插入到对应参数列之后:
library(tidyverse) library(lubridate) # 保留用户提供的原数据df和formula_1函数 df <- data.frame(...) # 此处为用户原数据,省略重复定义 formula_1 <- function(P, PET, Q) { Anomaly_P = P - mean(P, na.rm = TRUE) Anomaly_PET = PET - mean(PET, na.rm = TRUE) Anomaly_Q = Q - mean(Q, na.rm = TRUE) return(list(Anomaly_P = Anomaly_P, Anomaly_PET = Anomaly_PET, Anomaly_Q = Anomaly_Q)) } site_names <- sub(" P \\[mm\\]| PET \\[mm\\]| Q \\[mm\\]", "", names(df)[-1]) |> unique() # 初始化最终结果为原数据 df_final <- df # 循环处理每个站点,插入异常值列 for (site in site_names) { # 定义原参数列名 p_col <- paste0(site, " P [mm]") pet_col <- paste0(site, " PET [mm]") q_col <- paste0(site, " Q [mm]") # 计算异常值 anomalies <- formula_1(df[[p_col]], df[[pet_col]], df[[q_col]]) # 定义异常值列名 anom_p_col <- paste0(site, " Anomaly_P [mm]") anom_pet_col <- paste0(site, " Anomaly_PET [mm]") anom_q_col <- paste0(site, " Anomaly_Q [mm]") # 插入异常值列到对应参数列之后 p_pos <- which(names(df_final) == p_col) df_final <- add_column(df_final, !!anom_p_col := anomalies$Anomaly_P, .after = p_pos) pet_pos <- which(names(df_final) == pet_col) df_final <- add_column(df_final, !!anom_pet_col := anomalies$Anomaly_PET, .after = pet_pos) q_pos <- which(names(df_final) == q_col) df_final <- add_column(df_final, !!anom_q_col := anomalies$Anomaly_Q, .after = q_pos) }
结果说明
两种方式均能实现预期目标:原数据中每个站点的P [mm]列后追加对应Anomaly_P [mm]列,PET [mm]列后追加Anomaly_PET [mm]列,Q [mm]列后追加Anomaly_Q [mm]列,同时保持时序数据的行顺序不变。
内容的提问来源于stack exchange,提问作者Drop
相关产品推荐
相关产品推荐

