Functions I'm familiar with include scale from base R, rescale from ARM.
Perhaps the best way would be to use some variant of apply, specifying one or more variables to use as grouping variables.
Cross Validated is a question and answer site for people interested in statistics, machine learning, data analysis, data mining, and data visualization. It only takes a minute to sign up.
Sign up to join this communityFunctions I'm familiar with include scale from base R, rescale from ARM.
Perhaps the best way would be to use some variant of apply, specifying one or more variables to use as grouping variables.
Here is a possible plyr solution. Note that it relies on the base transform()
function.
my.df <- data.frame(x=rnorm(100, mean=10),
sex=sample(c("M","F"), 100, rep=T),
group=gl(5, 20, labels=LETTERS[1:5]))
library(plyr)
ddply(my.df, c("sex", "group"), transform, x.std = scale(x))
(We can check whether it works as expected with e.g., with(subset(my.df, sex=="F" & group=="A"), scale(x))
)
Basically, the 2nd argument describes how to "split" the data, the 3rd argument what function to apply to each chunk. The above will append a variable x.std
to the data.frame. Use x
if you want to replace your original variable by the scaled one.
group.center <- function(var,grp) {
return(var-tapply(var,grp,mean,na.rm=T)[grp])
}
Here is a data.table solution. It is definitely faster than plyr (relevant only for large data sets). Maybe later I'll do up a dplyr example.
# generate example data
raw.data <- data.frame( outcome = c(rnorm(500, 100, 15), rnorm(500, 110, 12)),
group = c(rep("a", 500), rep("b", 500)))
library(data.table)
# convert dataframe to data.table
raw.data <- data.table(raw.data, key = "group")
# create group standardized outcome variable
raw.data[ , group_std_outcome := (outcome - mean(outcome, na.rm = TRUE)) /
sd(outcome, na.rm = TRUE), "group"]
(Yes, I rediscovered a question I asked years ago when I was an R noob ;)
You can use (among others) tapply
for this (the plyr
package contains lots of other options that may be better suited for your specific situation):
tapply(variabletoscale, list(groupvar1, groupvar2), scale)
This answer is from a white paper by Mahmood Arai. It has the convenient side effect of labeling the centered results with the prefix "C.":
gcenter <- function(df1,group) {
variables <- paste(
rep("C", ncol(df1)), colnames(df1), sep=".")
copydf <- df1
for (i in 1:ncol(df1)) {
copydf[,i] <- df1[,i] - ave(df1[,i], group, FUN=mean)}
colnames(copydf) <- variables
return(cbind(df1,copydf))}
Here is an updated implementation using dplyr from tidyverse.
library(tidyverse)
my.df <- data.frame(x=rnorm(100, mean=10), sex=sample(c("M","F"), 100, rep=T))
my.df <- group_by(my.df, sex) %>% mutate(x.sd = as.numeric(scale(x)))