Reverse the order of non-NA values in a variable

Viewed 143

I am interested in reversing the values for a column that has NA values in a tidy way.

The rev call won't do the trick here:

library(tidyverse)
tibble(
Which = LETTERS[1:11],
x = c( c(3,1,4,2,16), NA, NA, 4, rep(NA, 2), 10)) %>% 
mutate(y = rev(x))

As it completely reverses the values (NAs included).

I essentially want a tidy mutate command (no splitting / joining) that reverses the values for the Which column so that E has value 1 (max becomes min) B has value 16 (min becomes max), etc - and NA values remain NA (F, G, I & J).

Edit:

Several answers do not achieve intended outcome. The question is aimed at effectively having a reverse (rev) work while keeping NAs in position.

@Moody_Mudskipper has a solution to the case where there's no repeats, but it fails when there are repeats, e.g.:

rev_na <- function(x) setNames(sort(x), sort(x, TRUE))[as.character(x)]

Works here:

tibble(
Which = LETTERS[1:11],
x = c( c(3,1,4,2,16), NA, NA, 4, rep(NA, 2), 10)) %>% 
mutate(y = rev_na(x))

Fails here:

tibble(
Which = LETTERS[1:7],
x = c(3,1,9,9,9, 9, 10)
) %>% mutate(y = rev_na(x), z = rev(x))
2 Answers

If you can tolerate a little hack :

tibble(
  Which = LETTERS[1:11],
  x = c( c(3,1,4,2,16), NA, NA, 4, rep(NA, 2), 10)) %>% 
  mutate(y = setNames(sort(x), sort(x, TRUE))[as.character(x)])
#> # A tibble: 11 x 3
#>    Which     x     y
#>    <chr> <dbl> <dbl>
#>  1 A         3     4
#>  2 B         1    16
#>  3 C         4     3
#>  4 D         2    10
#>  5 E        16     1
#>  6 F        NA    NA
#>  7 G        NA    NA
#>  8 H         4     3
#>  9 I        NA    NA
#> 10 J        NA    NA
#> 11 K        10     2

Created on 2021-05-11 by the reprex package (v0.3.0)

This will do

data.frame(
  Which = LETTERS[1:11],
  x = c( c(3,1,4,2,16), NA, NA, 4, rep(NA, 2), 10)) -> df

df %>% group_by(d = is.na(x)) %>%
  arrange(x) %>%
  mutate(y = ifelse(!d, rev(x), x)) %>%
  ungroup %>% select(-d)

# A tibble: 11 x 3
   Which     x     y
   <chr> <dbl> <dbl>
 1 B         1    16
 2 D         2    10
 3 A         3     4
 4 C         4     4
 5 H         4     3
 6 K        10     2
 7 E        16     1
 8 F        NA    NA
 9 G        NA    NA
10 I        NA    NA
11 J        NA    NA

Needless to say you may arrange back the results if your Which was arranged already or creating a row_number() at the start of the syntax.

df %>% 
  group_by(d = is.na(x)) %>%
  arrange(x) %>%
  mutate(y = ifelse(!d, rev(x), x)) %>%
  ungroup %>% select(-d) %>%
  arrange(Which)

# A tibble: 11 x 3
   Which     x     y
   <chr> <dbl> <dbl>
 1 A         3     4
 2 B         1    16
 3 C         4     4
 4 D         2    10
 5 E        16     1
 6 F        NA    NA
 7 G        NA    NA
 8 H         4     3
 9 I        NA    NA
10 J        NA    NA
11 K        10     2
Related