取一列中相加的数字的平均值

时间:2019-06-11 12:10:48

标签: r dplyr tidyverse

我的数据采用以下格式:

country gdp digits
US      100 2657
Aus     50  123
NZ      40  11

对于每个国家/地区中存储在digits列中的单个数字,我想取平均值。

这就是我所追求的:

country gdp digits mean_digits
US      100 2657   5
Aus     50  123    2
NZ      40  11     1

我想我应该将digits列拆分为单独的列中的各个数字,然后取算术平均值,但是我有点不确定,因为不同的行在{{1}中具有不同的位数}字段。

以下可复制数据的代码:

digits

6 个答案:

答案 0 :(得分:4)

我们需要一个将数字分解为数字并取均值的函数:

mean_digits = function(x) {
  sapply(strsplit(as.character(x), split = "", fixed = TRUE),
         function(x) mean(as.integer(x)))
}

df$mean_digits = mean_digits(df$digits)
df
#   country gdp digits mean_digits
# 1      US 100   2657           5
# 2     AUS  50    123           2
# 3      NZ  40     11           1

as.character()将数字输入转换为characterstrsplit将数字分为单个数字(结果为list),然后使用sapply转换为我们将每个列表元素转换为整数并取均值。

我们使用fixed = TRUE可以提高效率,因为我们不需要任何特殊的正则表达式来将每一位数字分开。

如果您经常使用此功能,则可能要round或检查输入是否为整数,如果由于NA而使输入具有小数,它将返回.

答案 1 :(得分:3)

1)捆扎:这种单行代码在gsubfn中使用strapply。它将每个数字转换为数字,然后取每个数字的平均值。

library(gsubfn)

transform(df, mean = sapply(strapply(digits, ".", as.numeric, simplify = TRUE), mean))

2)这有点长,但仍然是一个语句,不使用任何软件包。它在数字之间插入一个空格,使用read.table读取它们,然后应用rowMeans

transform(df, 
  mean = rowMeans(read.table(text = gsub("\\b", " ", digits), fill = NA), na.rm = TRUE))

答案 2 :(得分:2)

一种val foo:Future[Result] = aCoupleOfFutures.transform ( options => Created(json.Json.toJson(options)), exc => exc ) 可能是:

val result:Future[Result] = aCoupleOfFutures. someKindOfTransform {
    case Success(options) => Created(json.Json.toJson(options))
    case Failure(e) => BadRequest(e.getMessage)
}

或者:

tidyverse

答案 3 :(得分:2)

这里是stringr的替代方案。它使用sapplystr_extract_all来为每一行提取df$digits的字符并计算平均值。

library(stringr)
df$mean_digits <- sapply(str_extract_all(df$digits, ".{1}"), function(x) mean(as.numeric(x)))

df
  country gdp digits mean_digits
1      US 100   2657           5
2     AUS  50    123           2
3      NZ  40     11           1

或者,如果您确实想要,可以使用str_extract_allrowMeans的矩阵输出来完成。注意:对于str_extract_allsimplify = FALSE是默认设置。

extracted_mat <- str_extract_all(df$digits, ".{1}", simplify = TRUE)
class(extracted_mat) <- "numeric"

df$mean_digits <- rowMeans(extracted_mat, na.rm = T)

编辑:大规模运行基准测试(即使用@Gregor的示例建议)。

# Packages 
library(stringr)
library(gsubfn)

# Functions
mean_digits = function(x) {
  sapply(strsplit(as.character(x), split = "", fixed = TRUE),
         function(x) mean(as.integer(x)))
}
mnDigit <- function(x) {
  n <- nchar(x)
  sq <- as.numeric(paste0("1e", n:0))
  mean((x %% sq[-length(sq)]) %/% sq[-1])
}
mnDigit2 <- function(a) {
  dig <- ceiling(log10(a + 1))
  vec1 <- 10^(dig:1)
  vec2 <- vec1 / 10
  mean((a %% vec1) %/% vec2)
}

# Creating x
set.seed(1)
x = sample(1:1e7, size = 5e5)


microbenchmark::microbenchmark(mnDigit2=sapply(x, mnDigit2),
                               mnDigit=sapply(x, mnDigit),
                               stringr=sapply(str_extract_all(x, ".{1}"), function(x) mean(as.numeric(x))),
                               stringr_matrix = {
                                 extracted_mat <- str_extract_all(x, ".{1}", simplify = TRUE)
                                 class(extracted_mat) <- "numeric"
                                 rowMeans(extracted_mat, na.rm = T)
                               },
                               strsplit=mean_digits(x),
                               rowMeans=rowMeans(read.table(text = gsub("\\b", " ", x), fill = NA), na.rm = TRUE),
                               #strapply=sapply(strapply(x, ".", as.numeric, simplify=TRUE), mean),
                               times = 10)
Unit: milliseconds
           expr       min       lq     mean   median       uq      max neval  cld
       mnDigit2 3154.4249 3226.633 3461.847 3445.867 3612.690 3840.691    10   c 
        mnDigit 6403.7460 6613.345 6876.223 6736.304 6965.453 7634.197    10    d
        stringr 3277.0188 3628.581 3765.786 3711.022 3808.547 4347.229    10   c 
 stringr_matrix  944.5599 1029.527 1136.334 1090.186 1169.633 1540.976    10 a   
       strsplit 3087.6628 3259.925 3500.780 3416.607 3585.573 4249.027    10   c 
       rowMeans 1354.5196 1449.871 1604.305 1594.297 1745.088 1828.070    10  b 



identical(sapply(x, mnDigit2), sapply(x, mnDigit))
[1] TRUE
identical(sapply(x, mnDigit2), sapply(str_extract_all(x, ".{1}"), function(x) mean(as.numeric(x))))
[1] TRUE
identical(sapply(x, mnDigit2), {
  extracted_mat <- str_extract_all(x, ".{1}", simplify = TRUE)
  class(extracted_mat) <- "numeric"
  rowMeans(extracted_mat, na.rm = T)
})
[1] TRUE
identical(sapply(x, mnDigit2), mean_digits(x))
[1] TRUE
identical(sapply(x, mnDigit2), rowMeans(read.table(text = gsub("\\b", " ", x), fill = NA), na.rm = TRUE))
[1] TRUE

答案 4 :(得分:2)

另一个tidyverse单线,没有其他依赖项:

df %>% mutate(mean_digits =  map_dbl(strsplit(as.character(df$digits), ""), 
                                     ~ mean(as.numeric(.x))))
#   country gdp digits mean_digits
# 1      US 100   2657           5
# 2     AUS  50    123           2
# 3      NZ  40     11           1

说明

  1. 您使用strsplit将数字拆分为一个数字。这会为您提供一个列表,其中每个元素都包含个位数。
  2. 然后,您遍历此列表并计算这些数字的mean。在这里,我们使用map_dbl中的purrr,但是简单的sapply也可以解决问题。

或基于算术而非字符串拆分的解决方案:

df %>% mutate(mean_digits = 
                map_dbl(digits, 
                        ~ mean((.x %/% 10 ^ (0:(nchar(as.character(.x)) - 1)) %% 10))))

说明

您将每个数字整数(%/%除以10的幂(即10^010^110^2...10^i直到数字位数,然后将结果取10取模(精确地给您原始位数),然后计算平均值。


用于基准测试的裸函数

split_based <- function(x) {
   sapply(strsplit(as.character(x), ""), 
            function(.x) mean(as.numeric(.x)))
}

## split_based(df$digits)

arithmetic_based <- function(.x) {
   mean((.x %/% 10 ^ (0:(nchar(as.character(.x)) - 1)) %% 10))
}

## sapply(df$digits, arithmetic_based)

答案 5 :(得分:2)

使用算术可以更有效地完成此操作。

this solution的启发,我们可以做到:

mnDigit <- function(x) {
  n <- nchar(x)
  sq <- as.numeric(paste0("1e", n:0))
  mean((x %% sq[-length(sq)]) %/% sq[-1])
}

sapply(df$digits, mnDigit)
# [1] 5 2 1

说明:在函数nchar中,首先对数字进行计数并创建10的幂矢量。最后一行基本上以模为单位计算10的每个幂。

应用链接的答案中提到的“更通用的解决方案”看起来像这样(为修正错误,向 @thothal 致谢):

mnDigit2 <- function(a) {
  dig <- ceiling(log10(a + 1))
  vec1 <- 10^(dig:1)
  vec2 <- vec1 / 10
  mean((a %% vec1) %/% vec2)
}

让我们看一下基准:

  Unit: milliseconds
            expr        min         lq      mean    median         uq         max neval cld
mnDigit2          140.65468  152.48952  173.7740  171.3010  179.23491   248.25977    10  a 
mnDigit           130.21340  151.76850  185.0632  166.7446  193.03661   292.59642    10  a 
stringr           112.80276  116.17671  129.7033  130.6521  137.24450   149.82282    10  a 
strsplit          106.64857  133.76875  155.3771  138.6853  148.58234   257.20670    10  a 
rowMeans           27.58122   28.55431   37.8117   29.5755   41.82507    66.96972    10  a 
strapply         6260.85467 6725.88120 7673.3511 6888.5765 8957.92438 10773.54486    10   b
split_based       363.59171  432.15120  475.5603  459.9434  528.20592   623.79144    10  a 
arithmetic_based  137.60552  172.90697  195.4316  183.1395  208.44365   292.07671    10  a

注意:我删除了tidyverse解决方案,因为它们太嵌套了其他数据帧操作。

但是,这似乎是。实际上,rowMeans-read.table方法似乎是最快的。

数据

df <- structure(list(country = c("US", "AUS", "NZ"), gdp = c(100, 50, 
40), digits = c(2657, 123, 11)), class = "data.frame", row.names = c(NA, 
-3L))

基准代码

set.seed(42)
evav <- sample(1:1e5, size=1e4)

library(stringr)  # for str_extract_all
library(gsubfn)  # for strapply
microbenchmark::microbenchmark(mnDigit2=sapply(evav, mnDigit2),
                               mnDigit=sapply(evav, mnDigit2),
                               stringr=sapply(str_extract_all(evav, ".{1}"), function(x) mean(as.numeric(x))),
                               strsplit=mean_digits(evav),
                               rowMeans=rowMeans(read.table(text = gsub("\\b", " ", evav), fill = NA), na.rm = TRUE),
                               strapply=sapply(strapply(evav, ".", as.numeric, simplify=TRUE), mean),
                               split_based=sapply(evav, split_based),
                               arithmetic_based=sapply(evav, arithmetic_based),
                               times=10L,
                               control=list(warmup=10L))
# see `mean_digits` `split_based` & `arithmetic_based` functions in other answers