Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change NA into 0 based on other variable / how many times it was recorded

Tags:

r

I am still new to R and need help. I want to change the NA value in variables x1,x2,x3 to 0 based on the value of count. Count specifies the number of observations, and the x1,x2,x3 stand for the visit to the site (or replication). The value in each 'X' variable is the number of species found. However, not all sites were visited 3 times. The variable count is telling us how many times the site was actually visited. I want to identify the actual NA and real 0 (which means no species found). I want to change the NA into 0 if the site is actually visited and keep it NA if the site is not visited. For example from the dummy data, 'zhask' site is visited 2 times, then the NA in x1 of zhask needs to be replaced with 0.

This is the dummy data:

     site x1 x2 x3 count
1    miya  1  2  1     3
2   zhask NA  1 NA     2
3 balmond  3 NA  2     3
4   layla NA  1 NA     2
5  angela NA  3 NA     2

So, it the table need to be changed into:

     site x1 x2 x3 count
1    miya  1  2  1     3
2   zhask  0  1 NA     2
3 balmond  3  0  2     3
4   layla  0  1 NA     2
5  angela  0  3 NA     2

I've tried many things and try to make my own function, however, it is not working:

for(i in 1:nrow(df))
  {
  if( is.na(df$x1[i]) && (i < df$count[i]))
  {df$x1[i]=0} 
  else 
  {df$x1[i]=df$x1[i]}
}

this is the script for the dummy dataframe:

x1= c(1,NA,3, NA, NA)
x2= c(2,1, NA, 1, 3)
x3 = c(1, NA, 2, NA, NA)
count=c(3,2,3,2,2)
site=c("miya", "zhask", "balmond", "layla", "angela")
df=data.frame(site,x1,x2,x3,count)

Any help will be very much appreciated!

like image 611
Trifosa Iin Simamora Avatar asked Aug 04 '21 21:08

Trifosa Iin Simamora


People also ask

How to indicate NA in R?

In R, missing values are represented by the symbol NA (not available). Impossible values (e.g., dividing by zero) are represented by the symbol NaN (not a number). Unlike SAS, R uses the same symbol for character and numeric data. For more practice on working with missing data, try this course on cleaning data in R.

Can R read n a?

Missing values are represented in R by the NA symbol. NA is a special value whose properties are different from other values. NA is one of the very few reserved words in R: you cannot give anything this name. (Because R is case-sensitive, na and Na are okay to use, although I don't recommend them.)

Does column have NA in R?

In R, the easiest way to find columns that contain missing values is by combining the power of the functions is.na() and colSums(). First, you check and count the number of NA's per column. Then, you use a function such as names() or colnames() to return the names of the columns with at least one missing value.


Video Answer


3 Answers

One way to be to apply a function over all of your count columns. Here's a way to do that.

cols <- c("x1", "x2", "x3")
df[, cols] <- mapply(function(col, idx, count) {
  ifelse(idx <=count & is.na(col), 0, col)
}, df[,cols], seq_along(cols), MoreArgs=list(count=df$count))

#      site x1 x2 x3 count
# 1    miya  1  2  1     3
# 2   zhask  0  1 NA     2
# 3 balmond  3  0  2     3
# 4   layla  0  1 NA     2
# 5  angela  0  3 NA     2

We use mapply to iterate over the columns and the index of the column. We also pass in the count value each time (since it's the same for all columns, it goes in the MoreArgs= parameter). This mapply will return a list and we can use that to replace the columns with the updated values.

If you wanted to use dplyr, that might look more like

library(dplyr)
cols <- c("x1"=1, "x2"=2, "x3"=3)
df %>% 
  mutate(across(starts_with("x"), ~if_else(cols[cur_column()]<count & is.na(.x), 0, .x)))

I used the cols vector to get the index of the column which doesn't seem to be otherwise available when using across().

But a more "tidy" way to tackle this problem would be to pivot your data first to a "tidy" format. Then you can clean the data more easily and pivot back if necessary

library(dplyr)
library(tidyr)
df %>% 
  pivot_longer(cols=starts_with("x")) %>% 
  mutate(index=readr::parse_number(name)) %>%
mutate(value=if_else(index < count & is.na(value), 0, value)) %>% 
  select(-index) %>% 
  pivot_wider(names_from=name, values_from=value)

#   site    count    x1    x2    x3
#   <chr>   <dbl> <dbl> <dbl> <dbl>
# 1 miya        3     1     2     1
# 2 zhask       2     0     1    NA
# 3 balmond     3     3     0     2
# 4 layla       2     0     1    NA
# 5 angela      2     0     3    NA
like image 95
MrFlick Avatar answered Oct 21 '22 15:10

MrFlick


Via some indexing of the columns:

vars <- c("x1","x2","x3")
df[vars][is.na(df[vars]) & (col(df[vars]) <= df$count)] <- 0

#     site x1 x2 x3 count
#1    miya  1  2  1     3
#2   zhask  0  1 NA     2
#3 balmond  3  0  2     3
#4   layla  0  1 NA     2
#5  angela  0  3 NA     2

Essentially this is:

  1. selecting the variables/columns and storing in vars
  2. flagging the NA cells within those variables with is.na(df[vars])
  3. col(df[vars]) returns a column number for every cell, which can be checked if it is less than the df$count in each corresponding row
  4. the values meeting both the above criteria are overwritten <- with 0
like image 37
thelatemail Avatar answered Oct 21 '22 13:10

thelatemail


This could be yet another solution using purrr::pmap:

  • purrr::pmap is used for row-wise operations when applied on a data frame. It enables us to iterate over multiple arguments at the same time. So here c(...) refers to all corresponding elements of the selected variable (all except site) in each row

I think the rest of the solution is pretty clear but please let me know if I need to explain more about this.

library(dplyr)
library(purrr)
library(tidyr)

df %>%
  mutate(output = pmap(df[-1], ~ {x <- head(c(...), -1)
  inds <- which(is.na(x))
  req <- tail(c(...), 1) - sum(!is.na(x))
  x[inds[seq_len(req)]] <- 0
  x})) %>%
  select(site, output, count) %>%
  unnest_wider(output)

# A tibble: 5 x 5
  site       x1    x2    x3 count
  <chr>   <dbl> <dbl> <dbl> <dbl>
1 miya        1     2     1     3
2 zhask       0     1    NA     2
3 balmond     3     0     2     3
4 layla       0     1    NA     2
5 angela      0     3    NA     2
like image 21
Anoushiravan R Avatar answered Oct 21 '22 15:10

Anoushiravan R