Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate summary statistics (e.g. mean) on all numeric columns using data.table

Tags:

r

data.table

I have data with both numeric and non-numeric columns like this:

mydt
          vnum1 vint1 vfac1 vch1
 1: -0.30159484     8     3    E
 2: -0.09833430     8     1    D
 3: -2.15963282     1     3    D
 4:  0.03904374     5     2    B
 5:  1.54928970     4     1    C
 6: -0.73873654     5     1    A
 7: -0.68594479     9     2    B
 8:  1.35765612     1     2    E
 9:  1.46958351     2     1    B
10: -0.89623979     2     4    E

How can I select only numeric columns and calculate their mean using data.table?

I have tried this:

mydt[ , lapply(.SD, mean), ]
#        vnum1 vint1 vfac1 vch1
# 1: -0.046491   4.5    NA   NA
# Warning messages:
# 1: In mean.default(X[[3L]], ...) :
#  argument is not numeric or logical: returning NA
# 2: In mean.default(X[[4L]], ...) :
#  argument is not numeric or logical: returning NA


dput(mydt)
structure(list(vnum1 = c(-0.301594844692861, -0.0983343040483769, 
-2.15963282153076, 0.03904374068617, 1.54928969700272, -0.738736535236348, 
-0.685944791146016, 1.35765612481877, 1.46958350568506, -0.896239790653183
), vint1 = c(8L, 8L, 1L, 5L, 4L, 5L, 9L, 1L, 2L, 2L), vfac1 = structure(c(3L, 
1L, 3L, 2L, 1L, 1L, 2L, 2L, 1L, 4L), .Label = c("1", "2", "3", 
"4"), class = "factor"), vch1 = structure(c(5L, 4L, 4L, 2L, 3L, 
1L, 2L, 5L, 2L, 5L), .Label = c("A", "B", "C", "D", "E"), class = "factor")), .Names = c("vnum1", 
"vint1", "vfac1", "vch1"), class = c("data.table", "data.frame"
), row.names = c(NA, -10L), .internal.selfref = <pointer: 0x991c070>)

I have also tried the following, but it does not work:

 mydt[ , lapply(.SD, is.numeric),]
 #    vnum1 vint1 vfac1  vch1
 # 1:  TRUE  TRUE FALSE FALSE

mydt[,mydt[,lapply(.SD, is.numeric),]]
#    vnum1 vint1 vfac1  vch1
# 1:  TRUE  TRUE FALSE FALSE

mydt[ , mydt[ , lapply(.SD, is.numeric) , ], with = F]
# Error in Math.data.frame(j) : 
#  non-numeric variable in data frame: vnum1vint1vfac1vch1

mydt[ , c(mydt[ , lapply(.SD, is.numeric)), ], with = F]
# Error: unexpected ')' in "mydt[,c(mydt[,lapply(.SD, is.numeric))"

As suggested by @Arun, I tried following but cannot get a subset:

 xx <- mydt[ , lapply(.SD, is.numeric), ]
 xx
 #    vnum1 vint1 vfac1  vch1
 # 1:  TRUE  TRUE FALSE FALSE

 mydt[ , lapply(.SD, mean), .SDcols = xx]
 # Error in `[.data.table`(mydt, , lapply(.SD, mean), .SDcols = xx) : 
 # .SDcols should be column numbers or names

As suggested by @David, I tried following but get NULL values for non-numeric columns. I want to get a subset of mydt so that other columns are not even listed.

mydt[ , lapply(.SD, function(x) if(is.numeric(x)) mean(x))]
#       vnum1 vint1 vfac1 vch1
# 1: -0.046491   4.5  NULL NULL

I am missing data.frame:

sapply(mydf, is.numeric)
# vnum1 vint1 vfac1  vch1 
#  TRUE  TRUE FALSE FALSE 

mydf[sapply(mydf, is.numeric)]
#         vnum1 vint1
#1  -0.30159484     8
#2  -0.09833430     8
#3  -2.15963282     1
#4   0.03904374     5
#5   1.54928970     4
#6  -0.73873654     5
#7  -0.68594479     9
#8   1.35765612     1
#9   1.46958351     2
#10 -0.89623979     2


sapply(mydf[sapply(mydf, is.numeric)], mean)
#    vnum1     vint1 
#-0.046491  4.500000 

OK. Thanks to David's comment, following works:

mydt[, sapply(mydt, is.numeric), with = FALSE][,sapply(.SD, mean),]
#    vnum1     vint1 
# -0.046491  4.500000 

mydt[, sapply(mydt, is.numeric), with = FALSE]
#          vnum1 vint1
# 1: -0.30159484     8
# 2: -0.09833430     8
# 3: -2.15963282     1
# 4:  0.03904374     5
# ...
like image 866
rnso Avatar asked Sep 28 '14 08:09

rnso


People also ask

How do I create a summary statistics table in R?

The easiest way to create summary tables in R is to use the describe() and describeBy() functions from the psych library.

What is a summary statistic table?

The summary table is a visualization that summarizes statistical information about data in table form. The information is based on one data table in TIBCO Spotfire. You can, at any time, choose which measures you want to see (such as mean, median, etc.), as well as the columns on which to base these measures.

Which function will give you statistical summary?

R provides a wide range of functions for obtaining summary statistics. One method of obtaining descriptive statistics is to use the sapply( ) function with a specified summary statistic. Possible functions used in sapply include mean, sd, var, min, max, median, range, and quantile.


3 Answers

By searching on SO for .SDcols, I landed up on this answer, which I think explains quite nicely how to use it.

cols = sapply(mydt, is.numeric)
cols = names(cols)[cols]
mydt[, lapply(.SD, mean), .SDcols = cols]
#        vnum1 vint1
# 1: -0.046491   4.5

Doing mydt[, sapply(mydt, is.numeric), with = FALSE] (note: the "modern" way to do that is mydt[ , .SD, .SDcols = is.numeric])is not that efficient because it subsets your data.table with those columns and that makes a (deep) copy - more memory used unnecessarily.

And using colMeans coerces the data.table into a matrix, which again is not so memory efficient.

like image 179
Arun Avatar answered Oct 11 '22 20:10

Arun


You can use the below format in just one line without having to use sapply:

mydt[, lapply(.SD, mean), .SDcols = is.numeric]

Also, if you're working with real data, there's a good chance your data has na values. Here's how it'd work in case of NA's:

mydt[, lapply(.SD, function(i) mean(i, na.rm = T)), .SDcols = is.numeric]

like image 30
Moein Avatar answered Oct 11 '22 21:10

Moein


I got the same problem, also the code below may help too.

data("mtcars")
mtcars$X1 <- factor(mtcars$gear, levels = c(4,3,5)); str(mtcars) #create an non numeric column X1
my.mean <- function(x){ if(is.numeric(x)) c(mean(x), median(x))} 
my.df <- setNames(as.data.frame(unlist(lapply(mtcars, FUN = my.mean))), "values"); my.df
my.df$names <- rep(c("mean","median"), times = length(row.names(my.df))/2); my.df
my.df$variables <-  rownames(my.df); my.df
library(stringr)
my.df$variables <- str_remove(my.df$variables, "[12]"); my.df 

data_wide <- spread(my.df,  names, values)
data_wide

> data_wide
   variables       mean  median
1         am   0.406250   0.000
2       carb   2.812500   2.000
3        cyl   6.187500   6.000
4       disp 230.721875 196.300
5       drat   3.596563   3.695
6       gear   3.687500   4.000
7         hp 146.687500 123.000
8        mpg  20.090625  19.200
9       qsec  17.848750  17.710
10        vs   0.437500   0.000
11        wt   3.217250   3.325
like image 1
Seyma Kalay Avatar answered Oct 11 '22 21:10

Seyma Kalay