问题
I have a time series dataframe and would like to create a new numeric column with values which are a function of an existing numeric column and which are assigned according to the day of the week column.
For example, I would require something like the following code:
Day <- c("Mo", "Mo", "Mo", "Tu", "Tu", "We", "We", "We", "We", "Th")
Val <- c(1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000)
df <- data.frame(cbind(Day,Val))
df$Adj <- ifelse(df$Day == "Mo" || df$Day == "Tu",
as.numeric(levels(df$Val)) + 1,
as.numeric(levels(df$Val)) + 2)
to return:
Day Val Adj
1 Mo 1000 1001
2 Mo 1000 1001
3 Mo 1000 1001
4 Tu 1000 1001
5 Tu 1000 1001
6 We 1000 1002
7 We 1000 1002
8 We 1000 1002
9 We 1000 1002
10 Th 1000 1002
Unfortunately for me, my code instead returns Adj as a column of 1001s only.
Day Val Adj
1 Mo 1000 1001
2 Mo 1000 1001
3 Mo 1000 1001
4 Tu 1000 1001
5 Tu 1000 1001
6 We 1000 1001
7 We 1000 1001
8 We 1000 1001
9 We 1000 1001
10 Th 1000 1001
I've tested the ifelse on one of the "We" rows and it does the trick ...
> ifelse(df$Day[6] == "Mo" || df$Day[6] == "Tu",
+ as.numeric(levels(df$Val[6])) + 1,
+ as.numeric(levels(df$Val[6])) + 2)
[1] 1002
... but I can't seem to get it to work on an entire column, which I had understood is one of the advantages of the ifelse function over a looped if-else statement.
I'm basing my approach off of the most similar questions I could find (Create new column in dataframe using if {} else {} in R) but have had no joy. What am I missing here?
回答1:
With dplyr:
Day <- c("Mo", "Mo", "Mo", "Tu", "Tu", "We", "We", "We", "We", "Th")
Val <- c(1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000)
df <- data.frame(Day,Val)
library(dplyr)
df %>% rowwise %>% mutate(Adj = ifelse(Day == "Mo" || Day == "Tu",
Val + 1,
Val + 2))
#> # A tibble: 10 x 3
#> Day Val Adj
#> <chr> <dbl> <dbl>
#> 1 Mo 1000 1001
#> 2 Mo 1000 1001
#> 3 Mo 1000 1001
#> 4 Tu 1000 1001
#> 5 Tu 1000 1001
#> 6 We 1000 1002
#> 7 We 1000 1002
#> 8 We 1000 1002
#> 9 We 1000 1002
#> 10 Th 1000 1002
Note that df <- data.frame(cbind(Day,Val))
converts Val to character
which is probably not what you're looking for.
You can simplify this to df <- data.frame(Day,Val)
回答2:
We can use %in%
to check if Day
has value as c('Mo', 'Tu')
, add 1 or 2 accordingly to Val
.
df <- transform(df, Adj = Val + ifelse(Day %in% c('Mo', 'Tu'), 1, 2))
#you can do this without `ifelse` as well.
#df <- transform(df, Adj = Val + as.integer(!Day %in% c('Mo', 'Tu')) + 1)
df
# Day Val Adj
#1 Mo 1000 1001
#2 Mo 1000 1001
#3 Mo 1000 1001
#4 Tu 1000 1001
#5 Tu 1000 1001
#6 We 1000 1002
#7 We 1000 1002
#8 We 1000 1002
#9 We 1000 1002
#10 Th 1000 1002
回答3:
In attempting to respect this experience level associated with this question, those functions using loops that were used in the question were also used in providing the answer. One way to achieve the desired results can be done with the following code:
Adj <- list() # assign an empty list
for(i in 1: nrow(df)) { # For loop
if(df$Day[i] == "Mo" || df$Day[i] == "Tu") { # the conditional ifelse loop
Adj[i] <- c(1000 + 1) # The 1000 can be replaced w/ Val[i]
} else {
Adj[i] <- c(1000 + 2)
}
}
Adj <- as.numeric(paste0(Adj)) # Convert list to numeric vector
df <- cbind(df, Adj) # Combine the df with the new vector
df # print results
The outcome can be viewed from this link:
来源:https://stackoverflow.com/questions/63857194/use-of-ifelse-to-assign-values-to-a-new-dataframe-column-in-r