我有一系列值分别为1.2、2.5等的变量。我想用小数点分隔数字,以便为整数和小数点创建一个新列,然后分配总分。 / p>
HT_Q1 <- c(1.2, 2.5, 7.4)
HT_Q2 <- c(2.5, 8.5, 9.5)
AT_Q1 <- c(2.4, 1.2, 1.4)
AT_Q2 <- c(6.5, 1.5, 9.10)
df <- data.frame(HT_Q1, HT_Q2, AT_Q1, AT_Q2)
我可以使用mutate做到这一点
mutate(df,
HT_Q1_G = trunc(HT_Q1),
HT_Q1_B = HT_Q1 %% 1 * 10,
HT_Q1_P = (HT_Q1_G * 6) + HT_Q1_B)
但是,我想编写一个函数,因此不必为每个变量重复以上代码。是否可以将每个变量(HT_Q1,HT_Q2等)作为参数传递给函数并创建相应的变量(例如HT_Q2_G,HT_Q2_B,AT_Q2_G等)?
我试图根据传递给函数的参数创建变量名,但是它不起作用:
edit_score <- function(var){
mutate(df,
paste0(var, "_G") = trunc(var),
paste0(var, "_B") = var %% 1 * 10,
paste0(var, "_P") = (paste0(var, "_G") * 6) + paste0(var, "_B"))
}
edit_score(HT_Q1)
edit_score(HT_Q2)
edit_score(AT_Q1)
edit_score(AT_Q2)
我是R的新手,来自SAS背景,我习惯于使用宏编译器在执行代码之前先对代码中的文本进行调整。
答案 0 :(得分:2)
您可以在此处使用非标准评估:
library(dplyr)
library(purrr)
library(rlang)
edit_score <- function(var){
transmute(df,
!!paste0(var, "_G") := trunc(!!sym(var)),
!!paste0(var, "_B") := !!sym(var) %% 1 * 10,
!!paste0(var, "_P") := !!sym(paste0(var, "_G")) * 6 +
!!sym(paste0(var, "_B")))
}
bind_cols(df, map_dfc(names(df), edit_score))
sym
将列名的字符值转换为符号,并使用!!
对其进行求值。
最初很难理解非标准评估,在这种情况下,您也可以使用这种基本的R方法:
edit_score <- function(var){
col1 <- paste0(var, "_G")
col2 <- paste0(var, "_B")
col3 <- paste0(var, "_P")
df[[col1]] <- trunc(df[[var]])
df[[col2]] <- df[[var]] %% 1 * 10
df[[col3]] <- df[[col1]] * 6 + df[[col2]]
df[, c(col1, col2, col3)]
}
cbind(df, do.call(cbind, lapply(names(df), edit_score)))
答案 1 :(得分:1)
使用tidyverse时,以整齐的长格式存储数据非常重要。这使得使用tidyverse函数变得容易得多。使用collect函数,我们可以将您的数据转换为长格式,并且进行mutate操作会将这些函数应用于所有值。
HT_Q1 <- c(1.2, 2.5, 7.4)
HT_Q2 <- c(2.5, 8.5, 9.5)
AT_Q1 <- c(2.4, 1.2, 1.4)
AT_Q2 <- c(6.5, 1.5, 9.10)
df <- data.frame(HT_Q1, HT_Q2, AT_Q1, AT_Q2)
df <- df %>%
gather() %>%
mutate(G = trunc(value),
B = value %% 1 * 10,
P = G*6 + B)
# key value G B P
#1 HT_Q1 1.2 1 2 8
#2 HT_Q1 2.5 2 5 17
#3 HT_Q1 7.4 7 4 46
#4 HT_Q2 2.5 2 5 17
#5 HT_Q2 8.5 8 5 53
#6 HT_Q2 9.5 9 5 59
#7 AT_Q1 2.4 2 4 16
#8 AT_Q1 1.2 1 2 8
#9 AT_Q1 1.4 1 4 10
#10 AT_Q2 6.5 6 5 41
#11 AT_Q2 1.5 1 5 11
#12 AT_Q2 9.1 9 1 55
如果您确实想返回宽幅格式,尽管不建议这样做,则可以使用以下内容进行回退:
df <- df %>%
pivot_wider(id_cols = key, names_from = key, values_from = value:P, values_fn=list, , names_glue = "{key}_{.value}") %>%
unnest(cols=everything())
colnames(df) = gsub("_value", "", colnames(df))
# HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q2_G AT_Q1_G AT_Q2_G HT_Q1_B HT_Q2_B AT_Q1_B AT_Q2_B HT_Q1_P HT_Q2_P AT_Q1_P AT_Q2_P
#1 1.2 2.5 2.4 6.5 1 2 2 6 2 5 4 5 8 17 16 41
#2 2.5 8.5 1.2 1.5 2 8 1 1 5 5 2 5 17 53 8 11
#3 7.4 9.5 1.4 9.1 7 9 1 9 4 5 4 1 46 59 10 55
答案 2 :(得分:1)
您可以将across()
与lst()
的功能配合使用,该功能引用了先前创建的组件。
library(dplyr)
df %>%
mutate(across(.fns = lst( G = function(x) trunc(x),
B = function(x) x %% 1 * 10,
P = ~ (G(.) * 6) + B(.) )))
输出
across()
自动创建新的列名,并根据需要用"_"
分隔。您还可以通过.names
参数来自定义新的名称模式。
# HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q1_B HT_Q1_P
# 1 1.2 2.5 2.4 6.5 1 2 8
# 2 2.5 8.5 1.2 1.5 2 5 17
# 3 7.4 9.5 1.4 9.1 7 4 46
#
# HT_Q2_G HT_Q2_B HT_Q2_P AT_Q1_G AT_Q1_B AT_Q1_P
# 1 2 5 17 2 4 16
# 2 8 5 53 1 2 8
# 3 9 5 59 1 4 10
#
# AT_Q2_G AT_Q2_B AT_Q2_P
# 1 6 5 41
# 2 1 5 11
# 3 9 1 55
答案 3 :(得分:0)
# python-like string concatenation `+`
`%+%` <- function(str1, str2) {
paste0(str1, str2)
}
add_columns <- function(df, col) {
df[, col %+% "_G"] <- trunc(df[, col])
df[, col %+% "_B"] <- df[, col] %% 1 * 10
df[, col %+% "_P"] <- df[, col %+% "_G"] * 6 + df[, col %+% "_B"]
df
}
generate_GBP_columns <- function(df) {
for (col in names(df)) {
df <- add_columns(df, col)
}
df
}
generate_GBP_columns(df)
# HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q1_B HT_Q1_P HT_Q2_G HT_Q2_B HT_Q2_P
# 1 1.2 2.5 2.4 6.5 1 2 8 2 5 17
# 2 2.5 8.5 1.2 1.5 2 5 17 8 5 53
# 3 7.4 9.5 1.4 9.1 7 4 46 9 5 59
# AT_Q1_G AT_Q1_B AT_Q1_P AT_Q2_G AT_Q2_B AT_Q2_P
# 1 2 4 16 6 5 41
# 2 1 2 8 1 5 11
# 3 1 4 10 9 1 55