6

I need to subtract specified value from each list element in R. In this article is mentioned that for such tasks the apply family of functions are used instead of loops. I've tried following:

# Define list
> a = 1:20

# Substraact this from each element
> substract_me = 5

# Function for substracting
> substract = function(x,y) { ret = x-y; return(ret) }

# The problem is that I do not know how to access the current array element and pass it to substract function
lapply(a, substract)

Here is mentioned that the anonymous functions can be also used, but it did not worked for me and I get syntax error. Indeed it looks to me just like syntactic sugar. The problem remain the same, that I need some placeholder or something when I am using lapply function so I can access the current list element:

lapply(a, function([WHAT TO ADD HERE???],substract_me) substract([WHAT TO ADD HERE???],substract_me))

Here is something probably related but I did not figure out how stuff works from posted code snippets.

Community
  • 1
  • 1
Wakan Tanka
  • 7,542
  • 16
  • 69
  • 122

2 Answers2

18

Subtraction in R is vectorized

a = 1:20
substract_me = 5
a - substract_me
# [1] -4 -3 -2 -1  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

No need for apply functions here.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • May I ask when should I use *apply family of functions? – Wakan Tanka Jun 23 '15 at 22:15
  • @WakanTanka I believe that the apply functions are useful for true list-type objects in r. dataframes and data.tables are two examples of list based objects. – icj Jan 11 '18 at 17:49
4

The title of the original question asks for how to apply subtract to a list type r object, but the example given subtracts from a vector a = 1:20.

The approved answer by MrFlick is exactly correct for vectors, but does not work with a list. For example, subtracting from the following list of integer vectors fails.

a <- list(v1 = 1:10, v2 = -5:5, v3 = 1:5)
# $v1
# [1]  1  2  3  4  5  6  7  8  9 10
# $v2
# [1] -5 -4 -3 -2 -1  0  1  2  3  4  5
# $v3
# [1] 1 2 3 4 5
subtract_me <- 5
a - subtract_me # produces a non-numeric argument to binary operator error

lapply with an inline-defined anonymous function is an easy way to accomplish the desired subtraction on a list.

subtract_me <- 5
lapply(a, function(x, subt_amnt) x - subt_amnt, subt_amnt = subtract_me)
# $v1
# [1] -4 -3 -2 -1  0  1  2  3  4  5
# $v2
# [1] -10  -9  -8  -7  -6  -5  -4  -3  -2  -1   0
# $v3
# [1] -4 -3 -2 -1  0

the subtraction function can be defined outside of lapply. Error handling and other checks in the function would likely warrant a standalone function.

For more info on the uses of anonymous functions see http://adv-r.had.co.nz/Functional-programming.html#anonymous-functions

As Requested by MoonS, an alternative approach would be to write the function explicitly outside the lapply call.

subtract_first_element_only <- function(x, subt_amnt) {
  # insert any additional logic to determine the subtract amount or 
  # from which elements to subtract here
  vector_out <- c(x[1] - subt_amnt,x[2:length(x)])
  return(vector_out)
}

lapply(a, subtract_first_element_only, subt_amnt = subtract_me)

#
icj
  • 719
  • 6
  • 12
  • I am interested to know if we have subtract_me as a vector of three elements and we want to subtract each element of the list from the corresponding element of the vector using lapply. i.e. subtract_me <- c(5, 3, 4). we want to subtract 5 from v1, 3 from v2 and 4 from v3.how could that be achieved – Jisika Apr 13 '21 at 06:36
  • 1
    @Jisika I'd recommend checking out the mapply function. That lets you pass individual values from the list or vector into a function. – icj Apr 26 '21 at 22:00
  • @icj Thanks for sharing. Is there a way to only subtract from the first element, e.g. -4? Or subtract based on a certain attribute in a dataframe? – MoonS Jun 28 '21 at 16:25
  • @MoonS That is possible. You'll need to write a more robust function to use inside of lapply. The example I used here is for an anonymous, in line function, but any function sourced in your script could be used. I'll update my answer to show how that would work. – icj Jun 30 '21 at 19:56