如何在R的DataFrame中找出各公司数值>0的首尾月份?
你给出的tibble数据如下:
> trial # A tibble: 6 x 13 Company `Jan-17` `Feb-17` `Mar-17` `Apr-17` `May-17` `Jun-17` `Jul-17` `Aug-17` `Sep-17` <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 A 83. 0. 33. 52. 0. 91. 61. 0. 40. 2 B 0. 0. 0. 0. 0. 0. 0. 0. 0. 3 C 5. 69. 0. 63. 6. 58. 0. 0. 0. 4 D 48. 0. 0. 0. 0. 74. 93. 3. 18. 5 E 39. 0. 0. 0. 0. 0. 0. 0. 0. 6 F 0. 90. 0. 0. 0. 0. 0. 0. 0. # ... with 3 more variables: `Oct-17` <dbl>, `Nov-17` <dbl>, `Dec-17` <dbl>
针对你的需求,我先给你推荐更高效的tidyverse方案(特别适合处理数千行的大规模数据,比for循环效率高很多),之后再补充你问到的for循环实现方式:
一、更优的tidyverse解决方案
这个方法利用tidyr的宽转长功能和dplyr的分组聚合,代码简洁且性能出色,完全适配大数据场景:
首先加载必要的包:
library(dplyr) library(tidyr)
然后执行以下步骤:
- 将宽格式数据转为长格式,把每个月份列拆成单独的行记录
- 过滤掉所有数值为0的无效行
- 按公司分组,提取每组的第一个和最后一个非零月份
代码如下:
result <- trial %>% pivot_longer(cols = -Company, names_to = "Month", values_to = "Value") %>% filter(Value > 0) %>% group_by(Company) %>% summarise( First_nonzero_month = first(Month), Last_nonzero_month = last(Month) )
补充:保留全0公司的处理
如果有些公司所有月份数值都是0(比如例子里的B公司),上面的代码会自动排除这些公司。如果你想保留它们并标记为NA,可以调整代码:
result <- trial %>% pivot_longer(cols = -Company, names_to = "Month", values_to = "Value") %>% group_by(Company) %>% summarise( First_nonzero_month = if(all(Value == 0)) NA else first(Month[Value > 0]), Last_nonzero_month = if(all(Value == 0)) NA else last(Month[Value > 0]) )
二、for循环实现方式
虽然for循环在处理大数据时效率不如向量化操作,但如果你确实需要用for循环实现,也可以这样写:
首先初始化一个结果数据框,用来存储每个公司的计算结果:
# 获取所有公司名称 companies <- trial$Company # 创建空的结果数据框 result_loop <- data.frame( Company = companies, First_nonzero_month = NA_character_, Last_nonzero_month = NA_character_, stringsAsFactors = FALSE ) # 获取所有月份列的名称 month_cols <- colnames(trial)[-1] # 遍历每个公司 for(i in seq_along(companies)){ # 提取当前公司的所有月份数值 values <- trial[i, -1] %>% unlist() # 找到数值>0的列索引 nonzero_indices <- which(values > 0) if(length(nonzero_indices) > 0){ # 赋值第一个非零月份 result_loop$First_nonzero_month[i] <- month_cols[min(nonzero_indices)] # 赋值最后一个非零月份 result_loop$Last_nonzero_month[i] <- month_cols[max(nonzero_indices)] } # 没有非零值的公司保持NA,无需额外处理 }
代码解释
- 先提前提取公司列表和月份列名,避免在循环中重复计算,提升一点效率
- 每次循环取出当前公司的所有数值,用
which()定位非零值的位置 - 如果存在非零值,就取最小和最大索引对应的月份名;如果没有,保持初始的
NA
内容的提问来源于stack exchange,提问作者Anirudh Murali
相关产品推荐
相关产品推荐

