1

One column in my dataframe in R has a direction (either Left, Right, L, or R). If a row in this column is Left or L, I am trying to convert the number in the same row in a different column to a negative value. This is the code I have written so far:

for(i in 1:nrow(df)){
  if(is.na(df[i,7]==F)){
    if(df[i,7]=="Left"  | df[i,7]=="L"){
      if(is.numeric(df[i,11])==T){
        lapply(df[i,11], all.neg)
      }
    }
  }
}

I keep getting the following error message in return:

Error in if (df[i, 7] == "Left" | df[i, 7] == "L") { : 
  missing value where TRUE/FALSE needed

I have tried to do na.pass(df) to avoid stopping after a missing value, and I included the first if statement of is.na(), which seems unnecessary. I have also gone through and made sure there were not other values like "Null" that were not being properly coded as NA. I would greatly appreciate if someone knows how to fix this issue - thanks so much!

Here is a screenshot of what the data looks like. Basically, I need for all of the values to be changed to negative if the LSTOA Direction is Left or L. enter image description here

Here is the head of the data:

structure(list(`LSTOA Direction` = c("Left", "Left", "Left", 
"Right", "Left", "Left"), `Preop PA` = c(NA, "6.5", "13.3", NA, 
NA, "11.0"), `1st Erect` = c(NA, NA, "2.8", NA, "7.6", "2.8"), 
    `6M PO PA` = c(NA_character_, NA_character_, NA_character_, 
    NA_character_, NA_character_, NA_character_), `1Y PO PA` = c("7.5", 
    NA, "3.3", "5.5", NA, NA), `2Y PO PA` = c(NA, NA, "0.1", 
    "5.8", "7.2", "2.5"), `5Y PO PA` = c(NA, NA, NA, "3.9", "4.4", 
    NA), `10Y PO PA` = c("7.8", NA, NA, "2.6", NA, NA), `15Y PO PA` = c(NA, 
    NA, NA, "3.2", NA, NA)), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))
  • Hi, I think this could be achieved much more easily using ``dplyr``. Could you provide a reproducible example of your input and outut - it'll make it much easier for others to help you. Thanks. – user438383 Jul 09 '21 at 16:41
  • the function from the `dplyr` package you are looking for is `case_when`. Take a look at the documentation for it and see if that helps! – Nova Jul 09 '21 at 16:43
  • (1) In `if`, don't use `|` (single pipe) unless you wrap it in `any` or `all`, otherwise your `if` conditional is incorrect. In `if`, the conditional must always be length 1, always. Anything else brings on errors like you see (if `NA` or `NULL`) or `the condition has length > 1 ...`. (2) It would help immensely to have sample data and expected output. Please see https://stackoverflow.com/q/5963269, [mcve], and https://stackoverflow.com/tags/r/info – r2evans Jul 09 '21 at 16:43

2 Answers2

0

Sample data.

dat <- data.frame(A=c("L","Right","R","Left"), B=1:4, C=11:14, D=LETTERS[1:4])
dat
#       A B  C D
# 1     L 1 11 A
# 2 Right 2 12 B
# 3     R 3 13 C
# 4  Left 4 14 D

Base R

mult <- 1-2*grepl("^L", dat$A)
mult
# [1] -1  1  1 -1
isnum <- sapply(dat, is.numeric)
isnum
#     A     B     C     D 
# FALSE  TRUE  TRUE FALSE 
dat[isnum] <- lapply(dat[isnum], `*`, must)
# *** output flushed ***
dat[isnum] <- lapply(dat[isnum], `*`, mult)
dat
#       A  B   C D
# 1     L -1 -11 A
# 2 Right  2  12 B
# 3     R  3  13 C
# 4  Left -4 -14 D

dplyr

library(dplyr)
dat %>%
  mutate(across(where(is.numeric), ~ if_else(grepl("^L", A), -1, 1) * .))
#       A  B   C D
# 1     L -1 -11 A
# 2 Right  2  12 B
# 3     R  3  13 C
# 4  Left -4 -14 D
r2evans
  • 141,215
  • 6
  • 77
  • 149
  • I tried using both of these methods on the dataframe I have now posted a screenshot of above. The code runs without errors, but it does not change any of the values to negative. The only thing I changed was A to LSTOA Direction because that is what my column is named. Thanks! – Elizabeth Driskill Jul 09 '21 at 17:28
  • That's fine, but I can't work on an image of data. If you want people to be able to use your data, then please edit your question and paste the output from `dput(head(x))` in a [code block](https://stackoverflow.com/editing-help). – r2evans Jul 09 '21 at 17:33
  • I have updated to include the head of the data in a code block - thank you! – Elizabeth Driskill Jul 09 '21 at 17:39
  • Have you looked at your own data? All columns are `character`, nothing is numeric. – r2evans Jul 09 '21 at 17:39
  • For this sample, if you do a quick fix of `df[,-1] <- lapply(df[,-1], as.numeric)` (not guaranteed to be generic when your data changes), these then work. – r2evans Jul 09 '21 at 17:41
  • 1
    That worked - I should have checked that before everything oops :). Thanks for all of your help! – Elizabeth Driskill Jul 09 '21 at 17:44
0

If you have a data.frame where there are two columns, one with the directional indicator in the 7th position and one with a numeric value in the 11th position that you want to become negative then try this "vectorized" reassignment

 #Copy the column to be altered
 df$newval <- df[[11]]
 df$newval[ grepl("^L", df[[7]]) ] <- -abs( df$newval[ grepl("^L", df[[7]]) ])

Both the LHS and the RHS have the same logical index that will be TRUE when the first letter is a capital "L", so only those values get the negative-treatment. It will now be negative (even if it were negative to start with.) If you didn't want that to be the case, then drop the abs and it will be the sign-reversed value. What I'm doing here if getting around a loop with a vectorized methods. There is a logical vector, grepl("^L", df[[7]]), that determine whether row-value will be modified.

A couple of points to improve your coding. Never use T and F. They will trip you up if you ever forget that they can be replaced with non logical values. TRUE and FALSE can never be redefined. And it is useless to test for equality to T or TRUE when you use is.numeric. Instead of is.numeric(df[i,11])==T just use is.numeric(df[i,11]). However, you would nopt want to use is.numeric inside a look that is doing row tests or row assignments. A vector is all numeric or non-numeric. Test outside a loop for better efficiency. I see that r2evans has provided a MCVE for testing. Notice that he named it dat which is a practice you should follow. Otherwise you will continue to see the error: Error in df[[2]] : object of type 'closure' is not subsettable which will not make any sense until you realize that df is a function name for the density of the F distribution.

IRTFM
  • 258,963
  • 21
  • 364
  • 487