You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## the uneven argument is the ELSE somewhat similar to Oracle DECODE()
fcase(
iris$Sepal.Length>5, ">5",
iris$Sepal.Length<4, "<4",
as.character(iris$Sepal.Length)
)
## new special symbol similar to at the end of an actual case when statement
fcase(
iris$Sepal.Length>5, ">5",
iris$Sepal.Length<4, "<4",
.ELSE, as.character(iris$Sepal.Length)
)
When I first started using dplyr from a SQL background, I kept finding it surprising the else was implemented as TRUE.
... that I'd like to standardize for downstream tasks.
Let's say that I need to modify everything except for Canada and Mexico. Using dplyr::case_when(), I would do so like this...
dplyr::case_when(
countries=="USA"~"United States of America",
countries=="Britain"~"United Kingdom",
countries=="Russian Federation"~"Russia",
countries=="Trinidad-Tobago"~"Trinidad and Tobago",
countries=="Bahamas"~"The Bahamas",
countries=="Timor-Leste"~"East Timor",
countries=="UAE"~"United Arab Emirates",
countries=="Congo"~"Democratic Republic of the Congo",
countries=="Sao Tome"~"Sao Tome and Principe",
TRUE~countries
)
#> [1] "United States of America" "United Kingdom" #> [3] "Russia" "Trinidad and Tobago" #> [5] "The Bahamas" "Democratic Republic of the Congo"#> [7] "United Arab Emirates" "Sao Tome and Principe" #> [9] "East Timor" "Canada" #> [11] "Mexico"
but data.table::fcase() seems to require finding the values you want to leave as-is separately.
conditions<- c("USA", "Britain", "Russian Federation", "Trinidad-Tobago",
"Bahamas", "Congo", "UAE", "Sao Tome", "Timor-Leste")
(dont_modify<-!countries%in%conditions)
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUEdata.table::fcase(
countries=="USA", "United States of America",
countries=="Britain", "United Kingdom",
countries=="Russian Federation", "Russia",
countries=="Trinidad-Tobago", "Trinidad and Tobago",
countries=="Bahamas", "The Bahamas",
countries=="Timor-Leste", "East Timor",
countries=="UAE", "United Arab Emirates",
countries=="Congo", "Democratic Republic of the Congo",
countries=="Sao Tome", "Sao Tome and Principe",
dont_modify, countries
)
#> [1] "United States of America" "United Kingdom" #> [3] "Russia" "Trinidad and Tobago" #> [5] "The Bahamas" "Democratic Republic of the Congo"#> [7] "United Arab Emirates" "Sao Tome and Principe" #> [9] "East Timor" "Canada" #> [11] "Mexico"
@knapply example you presented is perfect example where lookup table should be prefered, it is much easier to maintain and cleaner to use. Then you do countries[countries_new_name, name := i.new_name, on="name"].
@jangorecki , countries[countries_new_name, name := i.new_name, on="name"] is nice, but :
-do you agree that fcase is much faster ? (there is actually a faster solution than fcase)
-how do you deal with missing values? (i.e. the default argument or TRUE) ?
-does your solution also work when the output values are vectors?
Thanks.
I have not idea if it is faster, but I would expect fcase to be slower. Unless we look at microseconds, then anything can be faster than [.data.table call. Update on join will use binary merge to match new values to rows, so it will scale well. fcase is lazy so can potentially avoid a lot of computation. There are many factors, but I think scaling up will be in favor of update-on-join.
If there is missing value, then the name is not being updated at all, so stays as it was.
Yes you are right for large vector [.data.table is around 30% faster than fcase.
The fastest solution is a vectorised switch, which cut the time by half on a single thread.
Odd for missing values I get NA, so it does not stay as it was.
Activity
ColeMiller1 commentedon Feb 27, 2020
Just a few SQL like alternatives:
When I first started using dplyr from a SQL background, I kept finding it surprising the else was implemented as TRUE.
2005m commentedon Feb 28, 2020
I can understand the need for a default vector, however in the example above i would have written the code as follows:
That avoids the overhead that is currently being implemented in the PR and mentionned by @jangorecki
2005m commentedon Feb 28, 2020
It also raises the question. Do we want the same behaviour in
fifelse?sindribaldur commentedon Feb 28, 2020
@2005m my example was bad, the default or
.ELSEcondition can't always be stated so easily.2005m commentedon Feb 28, 2020
A good example would be great.
shrektan commentedon Feb 28, 2020
I've already made the implementations that supports scalar condition and lazy-eval defaults in the PR above. Please take a look there.
sindribaldur commentedon Feb 28, 2020
Here is a different example.
fcase()by no means necessary, but makes for concise and clean-looking code.knapply commentedon Apr 26, 2020
Here's a simple example that may be helpful.
I have a vector of country names...
... that I'd like to standardize for downstream tasks.
Let's say that I need to modify everything except for Canada and Mexico. Using
dplyr::case_when(), I would do so like this...but
data.table::fcase()seems to require finding the values you want to leave as-is separately.jangorecki commentedon Apr 26, 2020
@knapply example you presented is perfect example where lookup table should be prefered, it is much easier to maintain and cleaner to use. Then you do
countries[countries_new_name, name := i.new_name, on="name"].knapply commentedon Apr 26, 2020
I think I must've oversimplified, so here's an expanded example that sticks with the country theme...
@jangorecki this kind of pattern-matching flexibility is more what I'm getting at.
2005m commentedon Apr 28, 2020
@jangorecki ,
countries[countries_new_name, name := i.new_name, on="name"]is nice, but :-do you agree that
fcaseis much faster ? (there is actually a faster solution thanfcase)-how do you deal with missing values? (i.e. the default argument or
TRUE) ?-does your solution also work when the output values are vectors?
Thanks.
jangorecki commentedon Apr 28, 2020
[.data.tablecall. Update on join will use binary merge to match new values to rows, so it will scale well. fcase is lazy so can potentially avoid a lot of computation. There are many factors, but I think scaling up will be in favor of update-on-join.nameis not being updated at all, so stays as it was.counties_new_nameneeds to be a list/data.table2005m commentedon Apr 28, 2020
Yes you are right for large vector
[.data.tableis around 30% faster thanfcase.The fastest solution is a vectorised switch, which cut the time by half on a single thread.
Odd for missing values I get
NA, so it does not stay as it was.2005m commentedon Apr 30, 2020
2005m commentedon May 9, 2020
2005m commentedon Jun 9, 2020
jangorecki commentedon Jun 9, 2020
2005m commentedon Jun 10, 2020
iagogv3 commentedon Aug 22, 2022
Is there any update on this? Also, the solution proposed in first comment
does not work to me since I am using
fcaseinside[.data.tableandby = .()group with different group sizes, so I obtainInstead, it works replacing
rep(TRUE, nrow(iris))withrep(TRUE, .N)davidbudzynski commentedon Jun 1, 2023
Has there been any progress made on this issue? I can confirm that @2005m's
nifworks exactly as expected.Fablepongiste commentedon Aug 1, 2023
This should really be looked into, it is a classical issue and for once dplyr way with case_when so much nicer.