compound conditions in across / numeric but not integer columns

This question is in part an R issue, but primarily an across issue.

Consider this code:

x <- tibble(x = rpois(1e2, 5), z = rnorm(1e2, 0, 1))
x %>% dplyr::mutate(across(where(is.numeric), round, 4))

My goal is to round the numeric columns, but not the integer columns, which of course need no rounding. The problem from an R perspective is that integer columns are numeric columns also. But this code further results in the integers being converted to numeric (albeit still being nominal integers in output), which is undesirable. I want the non-integer columns rounded and the integer columns left alone.

The goal is to specify an across statement that works for is.numeric but also enforces !is.integer.

For example:

dplyr::mutate(across(where(is.numeric & !is.integer), round, 4))

This doesn't work, and other variants I have tried (enclosing the conditions in c() or moving the parentheses around) haven't seemed to work either.

Any suggestions for (1) the best way to incorporate multiple conditions with where, and (2) while we are at it, how to operate on numeric vectors while carving out integer vectors from that same operation? The colwise vignette did not seem to address multiple conditions with where.

Thanks.

You can use anonymous function as follows

library(tidyverse)
x <- tibble(x = rpois(1e2, 5), z = rnorm(1e2, 0, 1))
x %>% mutate(across(where(function(x) is.numeric(x) & !is.integer(x)),round,4))
#> # A tibble: 100 x 2
#>        x       z
#>    <int>   <dbl>
#>  1     2 -0.0338
#>  2     5  0.905 
#>  3     8 -0.519 
#>  4     2  0.0848
#>  5     5 -0.230 
#>  6     2 -0.0602
#>  7     3  0.903 
#>  8     7 -0.126 
#>  9     4  0.228 
#> 10     4 -0.0189
#> # … with 90 more rows
1 Like

I believe there is is.double()

1 Like

Yes, I see is.double( ) is nice and simple. Thanks

is.double() nominally exists but does not work for whatever reason.

can you please demonstrate that it doesnt work ?

1 Like

is.double( ) seems work for this specific problem ( I guess it should work for other cases too).

x <- tibble(x = rpois(1e2, 5), z = rnorm(1e2, 0, 1))
x
#> # A tibble: 100 x 2
#>        x       z
#>    <int>   <dbl>
#>  1     8  0.0530
#>  2     6  2.38  
#>  3     9  1.19  
#>  4     2 -1.01  
#>  5     2  0.308 
#>  6     8 -1.69  
#>  7     8 -0.520 
#>  8     3  0.787 
#>  9     4 -0.494 
#> 10     9  0.0484
#> # … with 90 more rows
x %>% mutate(across(where(is.double),round,2))
#> # A tibble: 100 x 2
#>        x     z
#>    <int> <dbl>
#>  1     8  0.05
#>  2     6  2.38
#>  3     9  1.19
#>  4     2 -1.01
#>  5     2  0.31
#>  6     8 -1.69
#>  7     8 -0.52
#>  8     3  0.79
#>  9     4 -0.49
#> 10     9  0.05
#> # … with 90 more rows
1 Like

Actually, you are correct, it does work on the example.

It was not working for me on my actual data set and I assumed it would not work on the example either.

But, it looks like the reason is because I had an additional column in the actual data set with a date in it (class dttm). Then, it fails. So, I suppose if I may extend this, add a column for y=Sys.time(), and we once again need to do something else, unfortunately.

Thanks very much for the help.

interestingly, when a date is a column in the dataset, the earlier anonymous function query suggested above still works. Perhaps it is just more robust in some ways?

mutate(across(where(function(x) is.numeric(x) & !is.integer(x)),round,4))

I see your point. I see while anonymous works, the other one does not. I think, both are equivalent. But they are not. One interesting thing, I notice, date-time object is not numeric or integer but double !!! (see following code). That might be the reason behind they are not same.

x1<-Sys.time()
class(x1)
#> [1] "POSIXct" "POSIXt"
is.double(x1)
#> [1] TRUE
is.numeric(x1)
#> [1] FALSE
is.integer(x1)
#> [1] FALSE

Here is an approach where I make my own function to test for 'pure' doubles i.e, they are doubles but also they arent anything else too.

x <- tibble(x = rpois(1e2, 5), z = rnorm(1e2, 0, 1),
            y= rep(Sys.time(),1e2))

is_pure_double <- function(x) is.double(x) & length(class(x))==1

x%>% mutate(across(where(is_pure_double),
                   ~round(.,4)))
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.