计算数据框特定列中的数字和
Compute digit-sums in specific columns of a data frame
我正在尝试对数据框最后两列中的整数位求和。我找到了一个可以求和的函数,但我想我在应用该函数时可能会遇到问题 - 不确定?
Dataframe
a = c("a", "b", "c")
b = c(1, 11, 2)
c = c(2, 4, 23)
data <- data.frame(a,b,c)
#Digitsum function
digitsum <- function(x) sum(floor(x / 10^(0:(nchar(as.character(x)) - 1))) %% 10)
#Applying function
data[2:3] <- lapply(data[2:3], digitsum)
这是我得到的错误:
*Warning messages:
1: In 0:(nchar(as.character(x)) - 1) :
numerical expression has 3 elements: only the first used
2: In 0:(nchar(as.character(x)) - 1) :
numerical expression has 3 elements: only the first used*
您的函数 digitsum
目前可以很好地处理单个标量输入,例如
digitsum(32)
# [1] 5
但是,它不能接受向量输入,否则":"
会报错。您需要使用 Vectorize
:
向量化此函数
vec_digitsum <- Vectorize(digitsum)
然后它适用于矢量输入:
b = c(1, 11, 2)
vec_digitsum(b)
# [1] 1 2 2
现在您可以毫无问题地使用 lapply
。
@Zheyuan Li 的回答解决了你使用lapply
的问题。虽然我想补充几点:
Vectorize
只是对mapply
的包装,它不能给你向量化的性能。
可以改进函数本身以提高可读性:
见
digitsum <- function(x) sum(floor(x / 10^(0:(nchar(as.character(x)) - 1))) %% 10)
vec_digitsum <- Vectorize(digitsum)
sumdigits <- function(x){
digits <- strsplit(as.character(x), "")[[1]]
sum(as.numeric(digits))
}
vec_sumdigits <- Vectorize(sumdigits)
microbenchmark::microbenchmark(digitsum(12324255231323),
sumdigits(12324255231323), times = 100)
Unit: microseconds
expr min lq mean median uq max neval cld
digitsum(12324255231323) 12.223 12.712 14.50613 13.201 13.690 96.801 100 a
sumdigits(12324255231323) 13.689 14.667 15.32743 14.668 15.157 38.134 100 a
两个版本的性能相似,但第二个版本更容易理解。
有趣的是,Vectorize
包装器为单个输入增加了相当大的开销:
microbenchmark::microbenchmark(vec_digitsum(12324255231323),
vec_sumdigits(12324255231323), times = 100)
Unit: microseconds
expr min lq mean median uq max neval cld
vec_digitsum(12324255231323) 92.890 96.801 267.2665 100.223 108.045 16387.07 100 a
vec_sumdigits(12324255231323) 94.357 98.757 106.2705 101.445 107.556 286.00 100 a
这个函数的另一个优点是,如果你有非常大的字符串格式的数字,它仍然可以工作(删除 as.character
的小修改)。而第一个版本的函数会有大数字的问题或者可能会引入错误。
注意:起初我的基准测试是比较 OP 函数的矢量化版本和我的函数的非矢量化版本,这给我的错误印象是我的函数要快得多。原来这是由 Vectorize
开销引起的。
我正在尝试对数据框最后两列中的整数位求和。我找到了一个可以求和的函数,但我想我在应用该函数时可能会遇到问题 - 不确定?
Dataframe
a = c("a", "b", "c")
b = c(1, 11, 2)
c = c(2, 4, 23)
data <- data.frame(a,b,c)
#Digitsum function
digitsum <- function(x) sum(floor(x / 10^(0:(nchar(as.character(x)) - 1))) %% 10)
#Applying function
data[2:3] <- lapply(data[2:3], digitsum)
这是我得到的错误:
*Warning messages:
1: In 0:(nchar(as.character(x)) - 1) :
numerical expression has 3 elements: only the first used
2: In 0:(nchar(as.character(x)) - 1) :
numerical expression has 3 elements: only the first used*
您的函数 digitsum
目前可以很好地处理单个标量输入,例如
digitsum(32)
# [1] 5
但是,它不能接受向量输入,否则":"
会报错。您需要使用 Vectorize
:
vec_digitsum <- Vectorize(digitsum)
然后它适用于矢量输入:
b = c(1, 11, 2)
vec_digitsum(b)
# [1] 1 2 2
现在您可以毫无问题地使用 lapply
。
@Zheyuan Li 的回答解决了你使用lapply
的问题。虽然我想补充几点:
Vectorize
只是对mapply
的包装,它不能给你向量化的性能。可以改进函数本身以提高可读性:
见
digitsum <- function(x) sum(floor(x / 10^(0:(nchar(as.character(x)) - 1))) %% 10)
vec_digitsum <- Vectorize(digitsum)
sumdigits <- function(x){
digits <- strsplit(as.character(x), "")[[1]]
sum(as.numeric(digits))
}
vec_sumdigits <- Vectorize(sumdigits)
microbenchmark::microbenchmark(digitsum(12324255231323),
sumdigits(12324255231323), times = 100)
Unit: microseconds
expr min lq mean median uq max neval cld
digitsum(12324255231323) 12.223 12.712 14.50613 13.201 13.690 96.801 100 a
sumdigits(12324255231323) 13.689 14.667 15.32743 14.668 15.157 38.134 100 a
两个版本的性能相似,但第二个版本更容易理解。
有趣的是,Vectorize
包装器为单个输入增加了相当大的开销:
microbenchmark::microbenchmark(vec_digitsum(12324255231323),
vec_sumdigits(12324255231323), times = 100)
Unit: microseconds
expr min lq mean median uq max neval cld
vec_digitsum(12324255231323) 92.890 96.801 267.2665 100.223 108.045 16387.07 100 a
vec_sumdigits(12324255231323) 94.357 98.757 106.2705 101.445 107.556 286.00 100 a
这个函数的另一个优点是,如果你有非常大的字符串格式的数字,它仍然可以工作(删除 as.character
的小修改)。而第一个版本的函数会有大数字的问题或者可能会引入错误。
注意:起初我的基准测试是比较 OP 函数的矢量化版本和我的函数的非矢量化版本,这给我的错误印象是我的函数要快得多。原来这是由 Vectorize
开销引起的。