R函数警告排查:'numerical expression has 6 elements: only the first used'
生成随机出生日期时的警告原因分析
示例数据
example_data <- data.frame( ID = 1:6, Month.Of.birth = c("September", "April", "December", "June", "April", "September"), year.of.birth = c(1942, 1942, 1938, 1946, 1944, 1946) )
问题描述
尝试为每条记录随机分配出生日期的日部分,编写的函数单独运行正常,但传入数据框的整列后出现警告:
Warning in 1:num_days_in_month : numerical expression has 6 elements: only the first used
用户编写的代码如下:
generate_random_day_of_birth <- function(year, month) { if (is.na(year) || is.na(month)) { return(NA) } leap_year <- year %% 4 == 0 & (year %% 100 != 0 | year %% 400 == 0) num_days_in_month <- ifelse(month == "January", 31, ifelse(month == "February" & !leap_year, 28, ifelse(month == "February" & leap_year, 29, ifelse(month %in% c("April", "June", "September", "November"), 30, 31)))) random_day <- sample(1:num_days_in_month, 1) return(random_day) } example_data$day.of.birth <- generate_random_day_of_birth(example_data$year.of.birth, example_data$Month.Of.birth)
警告原因
- 函数设计与输入不匹配:你写的函数是针对单个
year和month值设计的,但调用时传入了长度为6的向量(整个数据列)。此时num_days_in_month会生成一个长度为6的向量,而R中1:向量这种写法只会取向量的第一个元素来生成序列,忽略其余元素,因此触发警告。 - 结果不符合预期:不仅会触发警告,
sample(1:num_days_in_month, 1)只会基于第一个元素对应的日期范围生成一个随机数,然后将这个数重复赋值给所有6条记录,完全达不到“每条记录随机分配日”的目的。
修正方案
方案1:用Vectorize包装函数,使其支持向量输入
Vectorize可以将原本处理单个值的函数转换为支持向量输入的函数,自动逐元素处理:
generate_random_day_of_birth <- Vectorize(function(year, month) { if (is.na(year) || is.na(month)) { return(NA) } leap_year <- year %% 4 == 0 & (year %% 100 != 0 | year %% 400 == 0) num_days_in_month <- ifelse(month == "January", 31, ifelse(month == "February" & !leap_year, 28, ifelse(month == "February" & leap_year, 29, ifelse(month %in% c("April", "June", "September", "November"), 30, 31)))) random_day <- sample(1:num_days_in_month, 1) return(random_day) }) example_data$day.of.birth <- generate_random_day_of_birth(example_data$year.of.birth, example_data$Month.Of.birth)
方案2:用dplyr按行处理
如果习惯用tidyverse风格,可以用rowwise()实现逐行计算:
library(dplyr) example_data <- example_data %>% rowwise() %>% mutate(day.of.birth = { if (is.na(year.of.birth) || is.na(Month.Of.birth)) { NA_integer_ } else { leap_year <- year.of.birth %% 4 == 0 & (year.of.birth %% 100 != 0 | year.of.birth %% 400 == 0) num_days <- case_when( Month.Of.birth == "January" ~ 31, Month.Of.birth == "February" & !leap_year ~ 28, Month.Of.birth == "February" & leap_year ~ 29, Month.Of.birth %in% c("April", "June", "September", "November") ~ 30, TRUE ~ 31 ) sample(1:num_days, 1) } }) %>% ungroup()
内容的提问来源于stack exchange,提问作者DW1310
相关产品推荐
相关产品推荐

