teogj
teogj

Reputation: 343

R: Is there any way to send down all the NAs from each column in a dataframe?

Given a dataframe like the following one:

farm_1       farm_2        farm_3
NA           chicken       NA
cow          lamb          NA
NA           NA            deer
lamb         pig           pig
NA           donkey        NA

I would like to reshape it so all the NAs each single column contains go to the lower positions, leaving the upper ones to the actual values. Hence, the dataframe in the example should become something like this:

farm_1       farm_2        farm_3
cow          chicken       deer
lamb         lamb          pig
NA           pig           NA
NA           donkey        NA
NA           NA            NA

Upvotes: 1

Views: 40

Answers (2)

stefan
stefan

Reputation: 125208

For what it's good for. A tidyverse approach to achieve your desired result may look like so:

d <- read.table(text = "farm_1       farm_2        farm_3
NA           chicken       NA
cow          lamb          NA
NA           NA            deer
lamb         pig           pig
NA           donkey        NA", header = TRUE)

library(dplyr)

mutate(d, across(everything(), sort, na.last = TRUE))
#>   farm_1  farm_2 farm_3
#> 1    cow chicken   deer
#> 2   lamb  donkey    pig
#> 3   <NA>    lamb   <NA>
#> 4   <NA>     pig   <NA>
#> 5   <NA>    <NA>   <NA>

If you want to preserve the order of the non-NA values (thanks to @Lucas for pointing that out) you could do:

mutate(d, across(everything(), ~ sort(forcats::fct_inorder(.x), na.last = TRUE)))
#>   farm_1  farm_2 farm_3
#> 1    cow chicken   deer
#> 2   lamb    lamb    pig
#> 3   <NA>     pig   <NA>
#> 4   <NA>  donkey   <NA>
#> 5   <NA>    <NA>   <NA>

Upvotes: 3

Ronak Shah
Ronak Shah

Reputation: 389155

You can use na.omit to drop NA values, subset the values so that the last values are appended by NA.

df[] <- lapply(df, function(x) na.omit(x)[1:length(x)])

#  farm_1  farm_2 farm_3
#1    cow chicken   deer
#2   lamb    lamb    pig
#3   <NA>     pig   <NA>
#4   <NA>  donkey   <NA>
#5   <NA>    <NA>   <NA>

Or if you prefer dplyr -

library(dplyr)
df %>% mutate(across(.fns = ~na.omit(.)[1:length(.)]))

Upvotes: 2

Related Questions