You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R语言for循环中利用增量子集计算分舍日平均体重?

问题描述

我有一个包含约40000条数据的数据集,记录了不同鸡舍(house)中鸡只养殖周期内的体重。核心变量包括:

  • House Number:1-4号鸡舍
  • Days in the Crop:连续数值,范围从0.000001天到约39天,部分鸡舍提前结束养殖
  • Weight:体重(单位:克),前0.5-0.8天无读数为NA,需要保留这些NA以展示从第0天开始的生长过程

我尝试用循环计算每个鸡舍每天的平均体重并存储到新数据框中。单鸡舍(House 1)的代码可以正常运行:

house_1 <- subset(df,house==1)

daily_averages <- data.frame(Day = 1:39, Average=NA)
for (i in 1:1){
            lower = 0
            upper = 1
            weight = house_1$weight
            days = house_1$days_in_crop
                        for (meanroll in 1:39) {
                          meanroll_val = mean(weight[days > lower & days < upper],na.rm=TRUE)
                          daily_averages$Average[meanroll] <- meanroll_val
                          lower=lower+1
                          upper=upper+1
              }
}

这里的逻辑是:lower为每日起始值(从0开始),upper为每日结束值(例如0到1之间的天数属于第1天),计算该区间内的平均体重。

但扩展到多鸡舍的双层循环时,结果无法正确存储,输出大量NaN:

house_ID <- unique(df$house)
daily_data <- data.frame(HouseNumber=NA, Day = rep(1:39,length(house_ID), Average= NA)
    
     for (house_no in 1:length(house_ID)){
       lower= 0
       upper = 1
       weight = df$weight
       days= df$days_in_crop
       house_N <- subset(df,house==house_no)
         with(house_N,
          for(meanroll in 1:39){
            meanroll_val = mean(weight[days > lower & days < upper],na.rm=TRUE)
            daily_data$Average[meanroll] <- meanroll_val
            lower=lower+1
            upper=upper+1
          })
     }

即使替换为print(meanroll_val),输出的156个结果(4*39)中也有大量NaN。我需要批量处理的方案,因为未来鸡舍数量会增加,复制单鸡舍代码不现实。

补充数据样例:
前20条数据:

structure(list(house = c(3L, 1L, 4L, 2L, 3L, 2L, 4L, 1L, 3L, 
1L, 4L, 2L, 2L, 3L, 4L, 1L, 3L, 1L, 4L, 2L), days_in_crop = c(0.000856481, 
0.001435185, 0.002025463, 0.003252315, 0.003657407, 0.003738426, 
0.004479167, 0.005451389, 0.007766204, 0.008333333, 0.008912037, 
0.010138889, 0.010497685, 0.010555556, 0.011365741, 0.012361111, 
0.01462963, 0.015231481, 0.015798611, 0.017025463), weight = c(NA_integer_, 
NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
NA_integer_, NA_integer_, NA_integer_, NA_integer_)), row.names = c(NA, 
20L), class = "data.frame")

日龄超过9.1天的前20条数据:

days_9_39 <- subset(df,days_in_crop > 9.1)
dput(head(days_9_39, 20))

structure(list(house = c(4L, 3L, 3L, 1L, 4L, 1L, 2L, 2L, 3L, 
3L, 1L, 4L, 1L, 2L, 2L, 3L, 4L, 1L, 4L, 1L), days_in_crop = c(9.101712963, 
9.101944444, 9.102847222, 9.10318287, 9.103229167, 9.106064815, 
9.106145833, 9.1065625, 9.10837963, 9.108900463, 9.109849537, 
9.110162037, 9.112581019, 9.113032407, 9.113159722, 9.114247685, 
9.11462963, 9.116435185, 9.117118056, 9.119236111), weight = c(244L, 
255L, 249L, 260L, 259L, 276L, NA, 260L, 254L, 254L, 244L, 265L, 
268L, NA, 300L, 250L, 239L, 255L, 251L, 258L)), row.names = 10262:10281, class = "data.frame")
解决方案

一、修复原循环代码

你的双层循环有两个核心问题:

  1. 没有正确定位daily_data的行索引,每次循环都覆盖了前39行的Average值,而非对应鸡舍的行
  2. 在with(house_N)中使用了全局的weight和days,而非鸡舍子集的数据

修改后的代码:

house_ID <- unique(df$house)
# 初始化完整数据框:每个鸡舍对应39天
daily_data <- expand.grid(HouseNumber = house_ID, Day = 1:39)
daily_data$Average <- NA

for (house_no in house_ID){
  lower <- 0
  upper <- 1
  # 取当前鸡舍的子集数据
  house_N <- subset(df, house == house_no)
  weight <- house_N$weight
  days <- house_N$days_in_crop
  
  for(meanroll in 1:39){
    # 计算当前区间的平均体重
    meanroll_val <- mean(weight[days > lower & days < upper], na.rm = TRUE)
    # 定位当前鸡舍+当前天数的行,赋值
    daily_data$Average[daily_data$HouseNumber == house_no & daily_data$Day == meanroll] <- meanroll_val
    lower <- lower + 1
    upper <- upper + 1
  }
}

说明:

  • 用expand.grid初始化数据框,确保每个鸡舍的39天都有对应的行
  • 每次循环只操作当前鸡舍的子集数据,避免全局变量干扰
  • 通过条件索引精准赋值,不会覆盖其他鸡舍的数据

二、更高效的Tidyverse批量处理方案

对于大量数据和未来扩展鸡舍的需求,Tidyverse的向量化操作比循环更高效且易维护:

library(dplyr)
library(tidyr)

daily_data <- df %>%
  # 按鸡舍分组
  group_by(house) %>%
  # 生成每个数据点对应的天数区间:days_in_crop在(lower, upper)即对应Day=lower+1
  mutate(Day = floor(days_in_crop) + 1) %>%
  # 过滤到1-39天的区间
  filter(Day %in% 1:39) %>%
  # 按鸡舍和天数分组,计算平均体重
  group_by(house, Day) %>%
  summarise(Average = mean(weight, na.rm = TRUE), .groups = "drop") %>%
  # 补全所有1-39天的行,缺失的天数填充NA
  complete(house, Day = 1:39, fill = list(Average = NA)) %>%
  # 重命名列保持一致
  rename(HouseNumber = house)

说明:

  • floor(days_in_crop)+1直接把连续的天数映射到对应的Day(比如0<days<1对应Day=1,1<days<2对应Day=2,以此类推)
  • complete函数自动补全每个鸡舍缺失的天数,确保每个鸡舍都有39天的数据
  • 全程向量化操作,无需手动循环,代码更简洁且性能更优

内容的提问来源于stack exchange,提问作者coghlan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 17:56:09