Digit Sum Function in R

Digit sum function in R

This should be better:

digitsum <- function(x) sum(floor(x / 10^(0:(nchar(x) - 1))) %% 10)

Function for summing digits using R

One loop-less option is to use cSplit from the splitstackshape package to separate your number into a vector with one element per character, then add up all the elements in the vector. This works for 1 (single_num) or many numbers.

library(splitstackshape)
single_num <- 17692
many <- c(12345, 678910, 222, 10, 7099998)

sums <- function(vec) {
print(rowSums(cSplit(as.data.frame(vec),1, sep = '', stripWhite = FALSE), na.rm = T))
}

sums(single_num)
==> 25
sums(many)
==> 15 31 6 1 51
  • install and load the splitstackshape package
  • convert the value or vector given to a dataframe
  • use the cSplit function on column 1 with an empty separator to create a column with a df for each character (colnames automatically added by cSplit)
  • calculate the sum across each row and print

If you put in one number there'll be one row and one result, whereas if you put in a vector of multiple numbers there'll be that many rows and results.

na.rm = T is needed when you put in values with different numbers of characters, because otherwise the shorter ones will end up with NAs in the later columns and rowSums() will return NA.

Large number digit sum

You need arbitrary precision numbers. a^b with R's numerics (double precision floats) can be only represented with limited precision and not exactly for sufficiently large input.

library(gmp)
a <- as.bigz(13)
b <- as.bigz(67)
sum(as.numeric(strsplit(as.character(a^b), split = "")[[1]]))
#[1] 328

Recursive function sum of digits R

In R, it is recommended to use Recall for creating a recursive function.

I am using @d.b's function, but demonstrating with Recall

getSum = function( i ) 
{
if (nchar(i) == 1){
return(i)
} else if (i < 0 ) {
"Please enter a positive number"
}else {
print(i)
Recall( i = floor(i/10)) +i%%10
}
}

getSum(0)
# [1] 0
getSum(1)
# [1] 1
getSum(-1)
# [1] "Please enter a positive number"
getSum(5)
# [1] 5
getSum(100)
# [1] 100
# [1] 10
# [1] 1
getSum( 23)
# [1] 23
# [1] 5

Sum of digits in a numeric matrix per row

We can do

 i1 <-  apply(m1, 1, function(x) {
v1 <- sum(unlist(lapply(strsplit(as.character(x), ""), as.numeric)))
v1 > 31 & v1 < 40})

m1[i1, , drop = FALSE]
# [,1] [,2] [,3] [,4] [,5] [,6]
#[1,] 12 15 18 21 24 27

Or

i1 <- sapply(strsplit(do.call(paste0, as.data.frame(m1)), ""), 
function(x) sum(as.integer(x)))
m1[i1, , drop = FALSE]

Or we can do

f1 <-  Vectorize(function(x) sum(floor(x / 10^(0:(nchar(x) - 1))) %% 10))
i1 <- rowSums(t(apply(m1, 1, f1))) %in% 31:40
m1[i1, , drop = FALSE]

data

m1 <- matrix(11:28, nrow = 3)

Weighted sum of digits in R

> number <-  1059
> x <- strsplit(as.character(number), "")[[1]]
> y <- seq_len(nchar(number))
> as.numeric(as.numeric(x) %*% y)
[1] 52

Function for multi-level Harshad Number in R

In the meantime I made some progress in simplifying and generalizing the Harshad function:

# Function to calculate the n-leveled Harshad Numbers for given integer number intervall
multi.step.harshed.number = function(numlevels, start, end)
{
digitsum = function(x) sum(floor(x / 10^(0:(nchar(x) - 1))) %% 10)
checkinteger = function (x) {
ifelse (x == as.integer(x), return (x), return(NA))
}
db = data.frame(start:end)
for (i in 1:numlevels) {
db[, (2*i)] = sapply(db[, (2*i)-1], FUN=digitsum)
db[, (2*i)+1] = db[, (2*i)-1] / db[, (2*i)]
db[, (2*i)+1] = sapply(db[, (2*i)+1], FUN=checkinteger)
db = na.omit(db)
if (nrow(db) == 0) break
}
return(db[,1])
}

EDIT: 2nd version - maybe following code is a bit more elegant and less busy:

multi.step.harshed.number = function(numlevels, start, end) {
is.integer= function(x) ifelse (x== as.integer(x), return (x), return(NA))
digitsum = function(x) sum(floor(x/10^(0:(nchar(x)-1))) %% 10)
digitsumratio= function(x) ifelse(is.integer(x/digitsum(x)), x/digitsum(x), NA)
multi.digitsumratio= function(iter, x) {
for (i in 1:iter) x= digitsumratio(x)
return (ifelse(is.na(x), FALSE, TRUE))
}
sequence=start:end
idx=sapply(sequence, FUN=function (x) multi.digitsumratio(numlevels, x))
sequence[idx]
}

EDIT: 3rd version - same as 2nd but just faster due to use of parallel package:

multi.step.harshed.number = function(numlevels, start, end) {
library(parallel)
processors= detectCores()
cl= makeCluster(processors)
is.integer= function(x) ifelse (x== as.integer(x), return (x), return(NA))
digitsum = function(x) sum(floor(x/10^(0:(nchar(x)-1))) %% 10)
digitsumratio= function(x) ifelse(is.integer(x/digitsum(x)), x/digitsum(x), NA)
multi.digitsumratio= function(iter, x) {
for (i in 1:iter) x= digitsumratio(x)
return (ifelse(is.na(x), FALSE, TRUE))
}
sequence=start:end
idx=parSapply(cl=cl,X=sequence, FUN=function (x) multi.digitsumratio(numlevels, x))
sequence[idx]
}

Trying: multi.step.harshed.number(42, 100, 10000) returns:

 [1]   100   108   120   162   180   200   210   216   240   243   270   300   324   360   378   400   405   420   432   450
[21] 480 486 500 540 600 630 648 700 720 756 800 810 840 864 900 972 1000 1080 1200 1296
[41] 1458 1620 1800 1944 2000 2100 2160 2400 2430 2700 2916 3000 3240 3402 3600 3780 4000 4050 4200 4320
[61] 4374 4500 4800 4860 5000 5400 5832 6000 6300 6480 6804 7000 7200 7290 7560 8000 8100 8400 8640 8748
[81] 9000 9720 10000


Related Topics



Leave a reply



Submit