List comprehension in R

前端 未结 7 1533
执笔经年
执笔经年 2020-12-24 11:48

Is there a way to implement list comprehension in R?

Like python:

sum([x for x in range(1000) if x % 3== 0 or x % 5== 0])

same in H

相关标签:
7条回答
  • 2020-12-24 11:51

    Another way

    sum(l<-(1:1000)[l %% 3 == 0 | l %% 5 == 0])
    
    0 讨论(0)
  • 2020-12-24 11:56

    This list comprehension of the form:

    [item for item in list if test]
    

    is pretty straightforward with boolean indexing in R. But for more complex expressions, like implementing vector rescaling (I know this can be done with scales package too), in Python it's easy:

    x = [1, 3, 5, 7, 9, 11] # -> [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
    [(xi - min(x))/(max(x) - min(x)) for xi in x]
    

    But in R this is the best I could come up with. Would love to know if there's something better:

    sapply(x, function(xi, mn, mx) {(xi-mn)/(mx-mn)}, mn = min(x), mx = max(x))
    
    0 讨论(0)
  • 2020-12-24 12:03

    You could convert a sequence of random numbers to a binary sequence as follows:

    x=runif(1000)
    y=NULL
    for (i in x){if (i>.5){y<-c(y,1)}else{y=c(y,-1)}}
    

    this could be generalized to operate on any list to another list based on:

    x = [item for item in x if test == True]
    

    where the test could use the else statement to not append the list y.

    For the problem at hand:

    x <- 0:999 
    y <- NULL 
    for (i in x){ if (i %% 3 == 0 | i %% 5 == 0){ y <- c(y, i) }}
    sum( y )
    
    0 讨论(0)
  • 2020-12-24 12:06

    Something like this?

    l <- 1:1000
    sum(l[l %% 3 == 0 | l %% 5 == 0])
    
    0 讨论(0)
  • 2020-12-24 12:10

    The foreach package by Revolution Analytics gives us a handy interface to list comprehensions in R. https://www.r-bloggers.com/list-comprehensions-in-r/

    Example

    Return numbers from the list which are not equal as tuple:

    Python

    list_a = [1, 2, 3]
    list_b = [2, 7]
    
    different_num = [(a, b) for a in list_a for b in list_b if a != b]
    
    print(different_num) 
    
    # Output: 
    [(1, 2), (1, 7), (2, 7), (3, 2), (3, 7)]
    

    R

    require(foreach)
    
    list_a = c(1, 2, 3)
    list_b = c(2, 7)
    
    different_num  <- foreach(a=list_a ,.combine = c ) %:% foreach(b=list_b) %:% when(a!=b) %do% c(a,b)
    
    print(different_num)
    
    # Output:
             [[1]]
             [1] 1 2
    
             [[2]]
             [1] 1 7
    
             [[3]]
             [1] 2 7
    
             [[4]]
             [1] 3 2
    
             [[5]]
             [1] 3 7
    

    EDIT:
    The foreach package is very slow for certain tasks. A faster list comprehension implementation is given at List comprehensions for R

    . <<- structure(NA, class="comprehension")
    
    comprehend <- function(expr, vars, seqs, guard, comprehension=list()){
      if(length(vars)==0){  # base case of recursion
        if(eval(guard)) comprehension[[length(comprehension)+1]] <- eval(expr)
      } else {
        for(elt in eval(seqs[[1]])){
          assign(vars[1], elt, inherits=TRUE)
          comprehension <- comprehend(expr, vars[-1], seqs[-1], guard, 
                                      comprehension)
        }
      }
      comprehension
    }
    
    ## List comprehensions specified by close approximation to set-builder notation:
    ##
    ##   { x+y | 0<x<9, 0<y<x, x*y<30 } ---> .[ x+y ~ {x<-0:9; y<-0:x} |  x*y<30 ]
    ##
    
    "[.comprehension" <- function(x, f,rectangularizing=T){
      f <- substitute(f)
      ## First, we pluck out the optional guard, if it is present:
      if(is.call(f) && is.call(f[[3]]) && f[[3]][[1]]=='|'){
        guard <- f[[3]][[3]]
        f[[3]] <- f[[3]][[2]]
      } else {
        guard <- TRUE
      }
      ## To allow omission of braces around a lone comprehension generator,
      ## as in 'expr ~ var <- seq' we make allowances for two shapes of f:
      ##
      ## (1)    (`<-` (`~` expr
      ##                   var)
      ##              seq)
      ## and
      ##
      ## (2)    (`~` expr
      ##             (`{` (`<-` var1 seq1)
      ##                  (`<-` var2 seq2)
      ##                      ...
      ##                  (`<-` varN <- seqN)))
      ##
      ## In the former case, we set gens <- list(var <- seq), unifying the
      ## treatment of both shapes under the latter, more general one.
      syntax.error <- "Comprehension expects 'expr ~ {x1 <- seq1; ... ; xN  <- seqN}'."
      if(!is.call(f) || (f[[1]]!='<-' && f[[1]]!='~'))
        stop(syntax.error)
      if(is(f,'<-')){ # (1)
        lhs <- f[[2]]
        if(!is.call(lhs) || lhs[[1]] != '~')
          stop(syntax.error)
        expr <- lhs[[2]]
        var <- as.character(lhs[[3]])
        seq <- f[[3]]
        gens <- list(call('<-', var, seq))
      } else { # (2)
        expr <- f[[2]]
        gens <- as.list(f[[3]])[-1]
        if(any(lapply(gens, class) != '<-'))
          stop(syntax.error)
      }
      ## Fill list comprehension .LC
      vars <- as.character(lapply(gens, function(g) g[[2]]))
      seqs <- lapply(gens, function(g) g[[3]])
      .LC <- comprehend(expr, vars, seqs, guard)
    
      ## Provided the result is rectangular, convert it to a vector or array
      if(!rectangularizing) return(.LC)
      tryCatch({
         if(!length(.LC))
          return(.LC)
      dim1 <- dim(.LC[[1]])
      if(is.null(dim1)){
        lengths <- sapply(.LC, length)
        if(all(lengths == lengths[1])){ # rectangular
          .LC <- unlist(.LC)
          if(lengths[1] > 1) # matrix
            dim(.LC) <- c(lengths[1], length(lengths))
        } else { # ragged
          # leave .LC as a list
        }
      } else { # elements of .LC have dimension
        dim <- c(dim1, length(.LC))
        .LC <- unlist(.LC)
        dim(.LC) <- dim
      }
      return(.LC)
      }, error = function(err) {
        return(.LC)
      })
    
    }
    

    This implementation is faster then foreach, it allows nested comprehension, multiple parameters and parameters scoping.

     N <- list(10,20)
    .[.[c(x,y,z)~{x <- 2:n;y <- x:n;z <- y:n} | {x^2+y^2==z^2 & z<15}]~{n <- N}]
    
    [[1]]
    [[1]][[1]]
    [1] 3 4 5
    
    [[1]][[2]]
    [1]  6  8 10
    
    
    [[2]]
    [[2]][[1]]
    [1] 3 4 5
    
    [[2]][[2]]
    [1]  5 12 13
    
    [[2]][[3]]
    [1]  6  8 10
    
    0 讨论(0)
  • 2020-12-24 12:11

    And, (kind of) the for-comprehension of scala:

    for(i in {x <- 1:100;x[x%%2 == 0]})print(i)
    
    0 讨论(0)
提交回复
热议问题