0

I would like to run a for loop over sf with multiple conditions.

  1. if value of current column=100, then remains as 100;
  2. if value from last column >0, then value of current column = value of last column minus 10;
  3. else, value = 0

data input

For example, from this:

Name Jan Feb Mar Apr
A 100 0 0 0
B 20 0 0 0
C 80 0 100 0

Become this:

Name Jan Feb Mar Apr
A 100 90 80 70
B 20 10 0 0
C 80 70 100 90

I wrote the following code

for (i in 3:(ncol(polys1)-1)){
  if (polys1[i]==100){polys1[i]<-100} 
  else if (polys1[i-1]>0){polys1[i]<-(polys1[i-1]-10)}
  else {polys1[i]<-0}
}

but give me the error:

Error in if (polys1[i] == 100) { : 
  argument is not interpretable as logical
In addition: Warning message:
In if (polys1[i] == 100) { :
  the condition has length > 1 and only the first element will be used

Any help would be much appreciated!

  • Please specify your mock data with `dput`. Also, you should clarify how you want to iterate, because in your example you are iterating just over the third column (which is literally the same as defining i = 3, and then running the code inside the loop). Also, please explain your second rule a little bit further. – JKupzig Mar 07 '22 at 11:04
  • @JKupzig Hi, apologies that because my data was sf object, I found that it would be too long if put in dput. I uploaded an image on example data in the question. I would want to iterate from the third column (because first column was row name, second column would remain the same as what it was), until the last second column (because the last column was geometry). – Phung Chee Chean Mar 07 '22 at 12:00

1 Answers1

1

Here's a way using tidyverse functions. Basically, doing the operations in long format and pivot back in wide format.

library(tidyverse)

df %>% 
  mutate(id = row_number()) %>% 
  pivot_longer(cols = -id) %>% 
  group_by(id, cumsum(value > 0)) %>% 
  mutate(value = first(value) - 10*(row_number()-1),
         value = ifelse(value > 0, value, 0)) %>% 
  pivot_wider(id_cols = id) %>% 
  ungroup() %>% 
  select(-id)

# A tibble: 3 x 4
    Jan   Feb   Mar   Apr
  <dbl> <dbl> <dbl> <dbl>
1   100    90    80    70
2    20    10     0     0
3    80    70   100    90

data

df <- structure(list(Jan = c(100L, 20L, 80L), Feb = c(0L, 0L, 0L), 
    Mar = c(0L, 0L, 100L), Apr = c(0L, 0L, 0L)), class = "data.frame", row.names = c(NA, 
-3L))
Maël
  • 45,206
  • 3
  • 29
  • 67
  • Hi thanks! I'm new to stackoverflow and apologies for not being clear on the question. My dataset was sf object and the last column contains geometry and the first column is row name. If you don't bother, can you kindly have a look at the image just uploaded in the question which shows the example data input looks like? Thanks... – Phung Chee Chean Mar 07 '22 at 11:53
  • 1
    Sometimes, `sf` are a bit difficult to handle. What you can do is use `st_drop_geometry` to pass the SF object to a dataframe and then transform back to a SF object. – Maël Mar 07 '22 at 11:57