R: Modify elements selectively

modify {purrr} | R Documentation

## Modify elements selectively

### Description

`modify()` is a short-cut for `x[] <- map(x, .f); return(x)`. `modify_if()` only modifies the elements of `x` that satisfy a predicate and leaves the others unchanged. `modify_at()` only modifies elements given by names or positions. `modify_depth()` only modifies elements at a given level of a nested data structure.

### Usage

    modify(.x, .f, ...)

    ## Default S3 method:
    modify(.x, .f, ...)

    modify_if(.x, .p, .f, ...)

    ## Default S3 method:
    modify_if(.x, .p, .f, ...)

    modify_at(.x, .at, .f, ...)

    ## Default S3 method:
    modify_at(.x, .at, .f, ...)

    modify_depth(.x, .depth, .f, ..., .ragged = .depth < 0)

    ## Default S3 method:
    modify_depth(.x, .depth, .f, ..., .ragged = .depth < 0)

### Arguments

`.x` | A list or atomic vector.
`.f` | A function, formula, or atomic vector. If a function, it is used as is. If a formula, e.g. `~ .x + 2`, it is converted to a function. There are three ways to refer to the arguments: For a single argument function, use `.` For a two argument function, use `.x` and `.y` For more arguments, use `..1`, `..2`, `..3` etc This syntax allows you to create very compact anonymous functions. If character vector, numeric vector, or list, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in `get-attr()` to extract named attributes. If a component is not present, the value of `.default` will be returned.
`...` | Additional arguments passed on to `.f`.
`.p` | A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as `.x`. Alternatively, if the elements of `.x` are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where `.p` evaluates to `TRUE` will be modified.
`.at` | A character vector of names or a numeric vector of positions. Only those elements corresponding to `.at` will be modified.
`.depth` | Level of `.x` to map on. Use a negative value to count up from the lowest level of the list. `modify_depth(x, 0, fun)` is equivalent to `x[] <- fun(x)` `modify_depth(x, 1, fun)` is equivalent to `x[] <- map(x, fun)` `modify_depth(x, 2, fun)` is equivalent to `x[] <- map(x, ~ map(., fun))`
`.ragged` | If `TRUE`, will apply to leaves, even if they're not at depth `.depth`. If `FALSE`, will throw an error if there are no elements at depth `.depth`.

### Details

Since the transformation can alter the structure of the input; it's your responsibility to ensure that the transformation produces a valid output. For example, if you're modifying a data frame, `.f` must preserve the length of the input.

### Value

An object the same class as `.x`

### Genericity

All these functions are S3 generic. However, the default method is sufficient in many cases. It should be suitable for any data type that implements the subset-assignment method `[<-`.

In some cases it may make sense to provide a custom implementation with a method suited to your S3 class. For example, a `grouped_df` method might take into account the grouped nature of a data frame.

### See Also

Other map variants: `imap`, `invoke`, `lmap`, `map2`, `map`

### Examples

    # Convert factors to characters
    iris %>%
      modify_if(is.factor, as.character) %>%
      str()

    # Specify which columns to map with a numeric vector of positions:
    mtcars %>% modify_at(c(1, 4, 5), as.character) %>% str()

    # Or with a vector of names:
    mtcars %>% modify_at(c("cyl", "am"), as.character) %>% str()

    list(x = rbernoulli(100), y = 1:100) %>%
      transpose() %>%
      modify_if("x", ~ update_list(., y = ~ y * 100)) %>%
      transpose() %>%
      simplify_all()

    # Modify at specified depth ---------------------------
    l1 <- list(
      obj1 = list(
        prop1 = list(param1 = 1:2, param2 = 3:4),
        prop2 = list(param1 = 5:6, param2 = 7:8)
      ),
      obj2 = list(
        prop1 = list(param1 = 9:10, param2 = 11:12),
        prop2 = list(param1 = 12:14, param2 = 15:17)
      )
    )

    # In the above list, "obj" is level 1, "prop" is level 2 and "param"
    # is level 3. To apply sum() on all params, we map it at depth 3:
    l1 %>% modify_depth(3, sum) %>% str()

    # modify() lets us pluck the elements prop1/param2 in obj1 and obj2:
    l1 %>% modify(c("prop1", "param2")) %>% str()

    # But what if we want to pluck all param2 elements? Then we need to
    # act at a lower level:
    l1 %>% modify_depth(2, "param2") %>% str()

    # modify_depth() can be with other purrr functions to make them operate at
    # a lower level. Here we ask pmap() to map paste() simultaneously over all
    # elements of the objects at the second level. paste() is effectively
    # mapped at level 3.
    l1 %>% modify_depth(2, ~ pmap(., paste, sep = " / ")) %>% str()

[Package purrr version 0.2.4 Index]