R函数规范化数据帧变量
R Function normalize data frame variable
我有一个包含连续变量和两个分类 ID 变量的数据集。我想扩展我的数据,使连续变量的值对于每个 ID 的特定值都为 1。我尝试将其实现为 R 中的一个函数,这样我就可以避免重复编码。
此外,我有一个在 Stata 中实现的重新缩放的示例:
gen value_var_i_k= value_var if ID1=="15t16" & ID2 =="AUS"
egen value_var_i_k_m = mean(value_var_i_k)
drop value_var_i_k
rename value_var_i_k_m value_var_i_k
gen value_var_k= value_var if ID1 =="15t16"
bys ID2: egen value_var_k_m = mean(value_var_k)
drop value_var_k
rename value_var_k_m value_var_k
gen value_var_i = value_var if ID2=="AUS"
bys ID1: egen value_var_i_m = mean(value_var_i)
drop value_var_i
rename value_var_i_m value_var_i
gen value_var_i_k_norm= value_var * value_var_i_k/(value_var_i*value_var_k)
我在 R 中尝试用新变量创建 value_var_i value_var_k
和 value_var_i_k
变量,但出现错误(不适用于 class "character" 的方法):
library(dplyr)
library(magrittr)
normalize<-function(var,data,i,k) {
varname <- paste("value", var , sep="_")
#Id columns and define variables to select
col1<-"ID1"
col2<-"ID2"
select_variables<-c(col2,col1,varname)
#name of the output variables
name_ik<-paste(i,k,sep="_")
name.ik<-paste(name_ik, "df", sep="_")
name.i<-paste(i, "df", sep="_")
name.k<-paste(k, "df", sep="_")
#my attempt to replicate the Stata code with dplyr
data %>% filter_(as.name(col1)==as.name(i) & as.name(col2)==as.name(k)) %>% select_( .dots=select_variables) %$% as.name( name.ik)
data %>% filter_(as.name(col1)==as.name(i)) %>% select_( .dots = select_variables ) %>% group_by_(as.name(col2)) %>%transform( interp(~mean(b, na.rm = TRUE),b=as.name(varname) )) %$% as.name(name.i)
data %>% filter_(as.name(col2)==as.name(k)) %>% select_( .dots = select_variables ) %>% group_by_(as.name(col1)) %>%transform( interp(~mean(b, na.rm = TRUE),b=as.name(varname) )) %$% as.name(name.k)
norm <- data[eval(substitute(varname)]*as.name(name.ik)/ ( as.name(name.i) * as.name(name.k) )
}
更新二: 计算步骤的最小工作示例:
两个表中的变量 value_var
相同。 Stata 代码用变量 value_var_i_m
和 value_var_k_m
替换了 value_var_k
和 value_var_i
变量。
然后value_var
被转化。
value_var ID1 ID2 value_var_i_k value_var_k value_var_k_m
1.154662 15t16 AUS 1.154662 1.154662 1.154662
1.070471 17t18 AUS 1.154662 . 1.154662
0.9643197 19 AUS 1.154662 . 1.154662
1.036398 20 AUS 1.154662 . 1.154662
1.084701 21t22 AUS 1.154662 . 1.154662
1.463215 15t16 AUT 1.154662 1.463215 1.463215
1.431824 17t18 AUT 1.154662 . 1.463215
1.276983 19 AUT 1.154662 . 1.463215
1.441925 20 AUT 1.154662 . 1.463215
1.506117 21t22 AUT 1.154662 . 1.463215
1.589491 15t16 BEL 1.154662 1.589491 1.589491
1.540076 17t18 BEL 1.154662 . 1.589491
1.188218 19 BEL 1.154662 . 1.589491
1.386074 20 BEL 1.154662 . 1.589491
1.48204 21t22 BEL 1.154662 . 1.589491
value_var ID1 ID2 value_var_i value_var_i_m
1.154662 15t16 AUS 1.154662 1.154662
1.589491 15t16 BEL . 1.154662
1.463215 15t16 AUT . 1.154662
1.070471 17t18 AUS 1.070471 1.070471
1.540076 17t18 BEL . 1.070471
1.431824 17t18 AUT . 1.070471
0.9643197 19 AUS 0.9643197 0.9643197
1.276983 19 AUT . 0.9643197
1.188218 19 BEL . 0.9643197
1.036398 20 AUS 1.036398 1.036398
1.441925 20 AUT . 1.036398
1.386074 20 BEL . 1.036398
1.084701 21t22 AUS 1.084701 1.084701
1.506117 21t22 AUT . 1.084701
1.48204 21t22 BEL . 1.084701
归一化值var如下(ID1级别“15t16”和ID2级别"AUS"的归一化):
ID1 ID2 value_var_i_k_norm
AUS 15t16 1
AUS 17t18 1
AUS 19 1
AUS 20 1
AUS 21t22 1
AUT 15t16 1
AUT 17t18 1.055508
AUT 19 1.044988
AUT 20 1.097901
AUT 21t22 1.09571
BEL 15t16 1
BEL 17t18 1.045116
BEL 19 .8951011
BEL 20 .9715319
BEL 21t22 .9925373
更新: 为了使规范化(或缩放)步骤更加清晰,我在这里以宽格式显示规范化后的数据。
首先我从下面的宽数据入手
Row-/Colnames 15t16 17t18 19t 20t 21t22
AUS 1.154662 1.070471 0.9643197 1.036398 1.084701
AUT 1.463215 1.431824 1.276983 1.441925 1.506117
BEL 1.589491 1.540076 1.188218 1.386074 1.48204
我将矩阵标准化为具有 AUS 值的行和具有 15t16 值的列。所以,我会得到
Row-/Colnames 15t16 17t18 19t 20t 21t22
AUS 1 1 1 1 1
AUT 1 1.055508 1.044988 1.097901 1.09571
BEL 1 1.045116 .8951011 .9715319 .9925373
所以,如果我根据你的编辑理解正确的话。
这是您的 "normalization" 例程:
If (ID1 == "AUS" | ID2 == "15t16") {value_var == 1}
else {value_var unchanged}
这很容易通过 ifelse
完成(假设输入 data.frame
命名为 df
):
df$value_var <- ifelse(df$ID1 == "AUS" | df$ID2 == "15t16", 1, df$value_var)
同样,我不愿意称其为 "normalization",但它确实符合您的描述。通常,在统计上下文中,归一化意味着缩放——即减去平均值并除以标准差。但你在这里似乎没有这样做。
我现在看到我尝试翻译 Stata 代码让我在 R 中误入歧途。归一化变量只是根据特定 ID 值过滤原始数据帧的结果,在下一步中我内部加入他们与原始数据框。
normalize<-function(data,var,col1,col2,i,k) {
varname <- paste("z.ik", var , sep="_")
var.i<-paste(var,i, sep="_")
var.k<-paste(var,k, sep="_")
var.ik<-paste(var.i,k, sep="_")
varname_i <- paste("z", var.i , sep="_")
varname_k <- paste("z", var.k , sep="_")
varname_ik <- paste("z", var.ik , sep="_")
d2<-select_(data, varname, col1, col2)
filter_crit2 = interp(~ filter_var1 %in% i & filter_var2 %in% k ,.values = list(filter_var1 = as.name(col1),filter_var2 = as.name(col2)))
filter_crit_k = interp(~ filter_var2 %in% k ,.values = list(filter_var2 = as.name(col2)))
filter_crit_i = interp(~ filter_var1 %in% i ,.values = list(filter_var1 = as.name(col1)))
select_variables.k<-c(col2,varname)
select_variables.i<-c(col1,varname)
results_ik<- data %>% filter_( filter_crit2 ) %>% select_( .dots=varname ) %>%rename_(.dots=setNames( varname, varname_ik))
results_ik<-cbind( results_ik ,d2)
results_i<- data %>% filter_( filter_crit_i ) %>% select_( .dots=select_variables.k) %>%rename_(.dots=setNames( varname, varname_i)) %>%inner_join( ., results_ik)
results_k<- data %>% filter_( filter_crit_k ) %>% select_( .dots=select_variables.i) %>%rename_(.dots=setNames( varname, varname_k)) %>% inner_join( ., results_i)
mutate_fn <- function(d_in, varval, varname_norm){
d_out = d_in %>%
mutate_(.dots = setNames( varval, varname_norm))
}
d_in=results_k
varname_norm<- paste("z", var, sep="_")
varname_norm <- paste(varname_norm,"norm", sep="_")
varval <- lazyeval::interp(~ var1* var2 / (var3 * var4),.values= list( var1=as.name(varname) , var2=as.name(varname_ik), var3=as.name(varname_i), var4=as.name(varname_k)))
data.norm = mutate_fn(d_in,varval,varname_norm )
n<-length(data.norm)
names(data.norm)[n]<-varname_norm
data.norm <-select_( data.norm,.dots=c(col1,col2,varname,varname_norm))
}
这太长了,不适合作为评论。但是我可以将您的 Stata 代码从 13 行减少到 5 行,我希望这样可以使您对使用 Stata 或 R 或两者的人所做的事情更加清楚。我保留您的代码块并添加注释。
* no need to use -egen- to create a single mean; use -summarize- results
summarize value_var if ID1=="15t16" & ID2=="AUS", meanonly
gen value_var_i_k = r(mean)
* evaluating ID1 == "15t16" yields 1 or 0; dividing by 0 yields missing values
* which are ignored, which is what you want
bys ID2: egen value_var_k = mean(value_var / (ID1=="15t16"))
* same device as previous block
bys ID1: egen value_var_i = mean(value_var / (ID2=="AUS"))
gen value_var_i_k_norm = value_var * value_var_i_k/(value_var_i * value_var_k)
事实上,我们可以进一步削减,但这个版本不太清楚:
bys ID2: egen value_var_k = mean(value_var / (ID1=="15t16"))
bys ID1: egen value_var_i = mean(value_var / (ID2=="AUS"))
summarize value_var if ID1=="15t16" & ID2=="AUS", meanonly
gen value_var_i_k_norm = value_var * r(mean)/(value_var_i * value_var_k)
对于除零设备,请参阅第 10 节 this article。另一种忽略某些观察结果的方法是
bys ID1: egen value_var_i = mean(cond(ID2=="AUS", value_var, .))
在同一篇文章的第 9 节中进行了讨论。
无论哪种方式,一个关键细节是 egen
的 mean()
函数会忽略缺失值。
我有一个包含连续变量和两个分类 ID 变量的数据集。我想扩展我的数据,使连续变量的值对于每个 ID 的特定值都为 1。我尝试将其实现为 R 中的一个函数,这样我就可以避免重复编码。
此外,我有一个在 Stata 中实现的重新缩放的示例:
gen value_var_i_k= value_var if ID1=="15t16" & ID2 =="AUS"
egen value_var_i_k_m = mean(value_var_i_k)
drop value_var_i_k
rename value_var_i_k_m value_var_i_k
gen value_var_k= value_var if ID1 =="15t16"
bys ID2: egen value_var_k_m = mean(value_var_k)
drop value_var_k
rename value_var_k_m value_var_k
gen value_var_i = value_var if ID2=="AUS"
bys ID1: egen value_var_i_m = mean(value_var_i)
drop value_var_i
rename value_var_i_m value_var_i
gen value_var_i_k_norm= value_var * value_var_i_k/(value_var_i*value_var_k)
我在 R 中尝试用新变量创建 value_var_i value_var_k
和 value_var_i_k
变量,但出现错误(不适用于 class "character" 的方法):
library(dplyr)
library(magrittr)
normalize<-function(var,data,i,k) {
varname <- paste("value", var , sep="_")
#Id columns and define variables to select
col1<-"ID1"
col2<-"ID2"
select_variables<-c(col2,col1,varname)
#name of the output variables
name_ik<-paste(i,k,sep="_")
name.ik<-paste(name_ik, "df", sep="_")
name.i<-paste(i, "df", sep="_")
name.k<-paste(k, "df", sep="_")
#my attempt to replicate the Stata code with dplyr
data %>% filter_(as.name(col1)==as.name(i) & as.name(col2)==as.name(k)) %>% select_( .dots=select_variables) %$% as.name( name.ik)
data %>% filter_(as.name(col1)==as.name(i)) %>% select_( .dots = select_variables ) %>% group_by_(as.name(col2)) %>%transform( interp(~mean(b, na.rm = TRUE),b=as.name(varname) )) %$% as.name(name.i)
data %>% filter_(as.name(col2)==as.name(k)) %>% select_( .dots = select_variables ) %>% group_by_(as.name(col1)) %>%transform( interp(~mean(b, na.rm = TRUE),b=as.name(varname) )) %$% as.name(name.k)
norm <- data[eval(substitute(varname)]*as.name(name.ik)/ ( as.name(name.i) * as.name(name.k) )
}
更新二: 计算步骤的最小工作示例:
两个表中的变量 value_var
相同。 Stata 代码用变量 value_var_i_m
和 value_var_k_m
替换了 value_var_k
和 value_var_i
变量。
然后value_var
被转化。
value_var ID1 ID2 value_var_i_k value_var_k value_var_k_m
1.154662 15t16 AUS 1.154662 1.154662 1.154662
1.070471 17t18 AUS 1.154662 . 1.154662
0.9643197 19 AUS 1.154662 . 1.154662
1.036398 20 AUS 1.154662 . 1.154662
1.084701 21t22 AUS 1.154662 . 1.154662
1.463215 15t16 AUT 1.154662 1.463215 1.463215
1.431824 17t18 AUT 1.154662 . 1.463215
1.276983 19 AUT 1.154662 . 1.463215
1.441925 20 AUT 1.154662 . 1.463215
1.506117 21t22 AUT 1.154662 . 1.463215
1.589491 15t16 BEL 1.154662 1.589491 1.589491
1.540076 17t18 BEL 1.154662 . 1.589491
1.188218 19 BEL 1.154662 . 1.589491
1.386074 20 BEL 1.154662 . 1.589491
1.48204 21t22 BEL 1.154662 . 1.589491
value_var ID1 ID2 value_var_i value_var_i_m
1.154662 15t16 AUS 1.154662 1.154662
1.589491 15t16 BEL . 1.154662
1.463215 15t16 AUT . 1.154662
1.070471 17t18 AUS 1.070471 1.070471
1.540076 17t18 BEL . 1.070471
1.431824 17t18 AUT . 1.070471
0.9643197 19 AUS 0.9643197 0.9643197
1.276983 19 AUT . 0.9643197
1.188218 19 BEL . 0.9643197
1.036398 20 AUS 1.036398 1.036398
1.441925 20 AUT . 1.036398
1.386074 20 BEL . 1.036398
1.084701 21t22 AUS 1.084701 1.084701
1.506117 21t22 AUT . 1.084701
1.48204 21t22 BEL . 1.084701
归一化值var如下(ID1级别“15t16”和ID2级别"AUS"的归一化):
ID1 ID2 value_var_i_k_norm
AUS 15t16 1
AUS 17t18 1
AUS 19 1
AUS 20 1
AUS 21t22 1
AUT 15t16 1
AUT 17t18 1.055508
AUT 19 1.044988
AUT 20 1.097901
AUT 21t22 1.09571
BEL 15t16 1
BEL 17t18 1.045116
BEL 19 .8951011
BEL 20 .9715319
BEL 21t22 .9925373
更新: 为了使规范化(或缩放)步骤更加清晰,我在这里以宽格式显示规范化后的数据。
首先我从下面的宽数据入手
Row-/Colnames 15t16 17t18 19t 20t 21t22
AUS 1.154662 1.070471 0.9643197 1.036398 1.084701
AUT 1.463215 1.431824 1.276983 1.441925 1.506117
BEL 1.589491 1.540076 1.188218 1.386074 1.48204
我将矩阵标准化为具有 AUS 值的行和具有 15t16 值的列。所以,我会得到
Row-/Colnames 15t16 17t18 19t 20t 21t22
AUS 1 1 1 1 1
AUT 1 1.055508 1.044988 1.097901 1.09571
BEL 1 1.045116 .8951011 .9715319 .9925373
所以,如果我根据你的编辑理解正确的话。
这是您的 "normalization" 例程:
If (ID1 == "AUS" | ID2 == "15t16") {value_var == 1}
else {value_var unchanged}
这很容易通过 ifelse
完成(假设输入 data.frame
命名为 df
):
df$value_var <- ifelse(df$ID1 == "AUS" | df$ID2 == "15t16", 1, df$value_var)
同样,我不愿意称其为 "normalization",但它确实符合您的描述。通常,在统计上下文中,归一化意味着缩放——即减去平均值并除以标准差。但你在这里似乎没有这样做。
我现在看到我尝试翻译 Stata 代码让我在 R 中误入歧途。归一化变量只是根据特定 ID 值过滤原始数据帧的结果,在下一步中我内部加入他们与原始数据框。
normalize<-function(data,var,col1,col2,i,k) {
varname <- paste("z.ik", var , sep="_")
var.i<-paste(var,i, sep="_")
var.k<-paste(var,k, sep="_")
var.ik<-paste(var.i,k, sep="_")
varname_i <- paste("z", var.i , sep="_")
varname_k <- paste("z", var.k , sep="_")
varname_ik <- paste("z", var.ik , sep="_")
d2<-select_(data, varname, col1, col2)
filter_crit2 = interp(~ filter_var1 %in% i & filter_var2 %in% k ,.values = list(filter_var1 = as.name(col1),filter_var2 = as.name(col2)))
filter_crit_k = interp(~ filter_var2 %in% k ,.values = list(filter_var2 = as.name(col2)))
filter_crit_i = interp(~ filter_var1 %in% i ,.values = list(filter_var1 = as.name(col1)))
select_variables.k<-c(col2,varname)
select_variables.i<-c(col1,varname)
results_ik<- data %>% filter_( filter_crit2 ) %>% select_( .dots=varname ) %>%rename_(.dots=setNames( varname, varname_ik))
results_ik<-cbind( results_ik ,d2)
results_i<- data %>% filter_( filter_crit_i ) %>% select_( .dots=select_variables.k) %>%rename_(.dots=setNames( varname, varname_i)) %>%inner_join( ., results_ik)
results_k<- data %>% filter_( filter_crit_k ) %>% select_( .dots=select_variables.i) %>%rename_(.dots=setNames( varname, varname_k)) %>% inner_join( ., results_i)
mutate_fn <- function(d_in, varval, varname_norm){
d_out = d_in %>%
mutate_(.dots = setNames( varval, varname_norm))
}
d_in=results_k
varname_norm<- paste("z", var, sep="_")
varname_norm <- paste(varname_norm,"norm", sep="_")
varval <- lazyeval::interp(~ var1* var2 / (var3 * var4),.values= list( var1=as.name(varname) , var2=as.name(varname_ik), var3=as.name(varname_i), var4=as.name(varname_k)))
data.norm = mutate_fn(d_in,varval,varname_norm )
n<-length(data.norm)
names(data.norm)[n]<-varname_norm
data.norm <-select_( data.norm,.dots=c(col1,col2,varname,varname_norm))
}
这太长了,不适合作为评论。但是我可以将您的 Stata 代码从 13 行减少到 5 行,我希望这样可以使您对使用 Stata 或 R 或两者的人所做的事情更加清楚。我保留您的代码块并添加注释。
* no need to use -egen- to create a single mean; use -summarize- results
summarize value_var if ID1=="15t16" & ID2=="AUS", meanonly
gen value_var_i_k = r(mean)
* evaluating ID1 == "15t16" yields 1 or 0; dividing by 0 yields missing values
* which are ignored, which is what you want
bys ID2: egen value_var_k = mean(value_var / (ID1=="15t16"))
* same device as previous block
bys ID1: egen value_var_i = mean(value_var / (ID2=="AUS"))
gen value_var_i_k_norm = value_var * value_var_i_k/(value_var_i * value_var_k)
事实上,我们可以进一步削减,但这个版本不太清楚:
bys ID2: egen value_var_k = mean(value_var / (ID1=="15t16"))
bys ID1: egen value_var_i = mean(value_var / (ID2=="AUS"))
summarize value_var if ID1=="15t16" & ID2=="AUS", meanonly
gen value_var_i_k_norm = value_var * r(mean)/(value_var_i * value_var_k)
对于除零设备,请参阅第 10 节 this article。另一种忽略某些观察结果的方法是
bys ID1: egen value_var_i = mean(cond(ID2=="AUS", value_var, .))
在同一篇文章的第 9 节中进行了讨论。
无论哪种方式,一个关键细节是 egen
的 mean()
函数会忽略缺失值。