R语言循环生成子数据帧遇赋值错误及处理求助
解决dplyr循环生成子数据帧的问题
问题根源
你遇到的核心问题是在dplyr管道里直接使用列索引i时,dplyr并没有把它识别为对应的列,而是当成了数值本身。比如arrange(desc(i))其实是在排序数值i(比如循环到第4列时i=4),而不是column4这一列的值;select(my_names, i)也是取第i列,但后续的mutate(i=round(i))会新增一列叫i,值都是当前的索引数值,这就导致所有处理逻辑都没作用到目标列上。
正确解决方案
这里提供两种可行的方法,第一种是基于你原来的for循环+assign修改,第二种是更符合tidyverse风格的purrr::map方法:
方法1:修改for循环,用符号引用列
我们需要用rlang::sym()把列名转换成dplyr能识别的符号,再用!!(bang-bang操作符)注入到管道里:
library(dplyr) top <- 2 # 循环列名而不是索引,更直观 for(col_name in colnames(df)[-1]) { # 将列名转为符号 col_sym <- sym(col_name) # 生成子数据帧 sub_df <- df %>% arrange(desc(!!col_sym)) %>% select(my_names, !!col_sym) %>% top_n(top, !!col_sym) %>% mutate(!!col_sym := round(!!col_sym)) # 使用:=来给符号列赋值 # 赋值到对应变量名 assign(paste("top", col_name, sep="_"), sub_df) }
现在查看top_column4就能得到正确结果:
> top_column4 my_names column4 1 N 92 2 Y 72
方法2:用purrr::map生成列表(推荐)
如果你不想生成一堆零散的变量,更推荐把所有子数据帧存在一个列表里,方便后续操作:
library(purrr) library(dplyr) top <- 2 # 对除第一列外的每一列应用处理函数 top_dfs <- map(colnames(df)[-1], function(col_name) { col_sym <- sym(col_name) df %>% arrange(desc(!!col_sym)) %>% select(my_names, !!col_sym) %>% top_n(top, !!col_sym) %>% mutate(!!col_sym := round(!!col_sym)) }) # 给列表元素命名 names(top_dfs) <- paste("top", colnames(df)[-1], sep="_")
之后你可以通过top_dfs$top_column2来访问对应的子数据帧,这种方式比零散变量更整洁,尤其当列数多的时候。
补充说明
!!操作符的作用是把符号“注入”到dplyr的函数中,让dplyr知道我们要引用的是列,而不是变量本身。mutate里用!!col_sym :=而不是=,因为左边是符号,需要用:=来完成赋值。- 用列名循环比索引更直观,也避免了索引和列名对应出错的问题。
内容的提问来源于stack exchange,提问作者Paula
相关产品推荐
相关产品推荐

