linden
linden

Reputation: 137

Automatically print message when data.frame updates

I'd like for R to print the dimensions of my updated dataframes to show how each of my steps influences the number of observations remaining. Typically my dataset size changes because there are NAs. I can always quickly check dimensions:

a = data.frame(a = c(1,2), b = c(3, NA))
na.omit(a) %>% dim

but I would want to do two things in that one line:

na.omit(a) %>% size
a <- na.omit(a)

since code gets messy with many dim() calls.

Is there a way to toggle R's output? Something like

a <- na.omit(a)
message: dimensions are now m x n

Upvotes: 3

Views: 106

Answers (2)

Dason
Dason

Reputation: 61903

Edit: Added code to only display notification when the object changes under the original code.

If you just want the dimensions to print after every line that you execute you could write a taskCallBack

tc <- addTaskCallback(function(...){message("Dimensions are: ", paste(dim(a), collapse = " "));return(TRUE)})

# When you want to remove the callback
removeTaskCallback(tc)

Here is an example session of it in use. You could probably modify the taskCallBack to only print if things change.

> a <- mtcars
> 
> tc <- addTaskCallback(function(...){message("Dimensions are: ", paste(dim(a), collapse = " "));return(TRUE)})
Dimensions are: 32 11
> 
> a
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
Dimensions are: 32 11
> a <- a[1:5,]
Dimensions are: 5 11
> 
> # When you want to remove the callback
> removeTaskCallback(tc)
[1] TRUE

If you want to only receive notifications when the object changes we can write a little bit more to actually monitor the object and only display the message when appropriate. Here is the code I'll use:

# @object_name - Character string. The object name you want to montior
# @example tc <- addTaskCallback(monitor_object("mtcars"))
monitor_object <- function(object_name){
    object_dim <- NULL
    if(exists(object_name)){
        object_dim <- dim(get(object_name))
    }

    f <- function(...){
        new_dim <- NULL
        if(exists(object_name)){
            new_dim <- dim(get(object_name))
        }
        if(!identical(new_dim, object_dim)){


            msg <- paste0(object_name, 
                         " changed.\nOld dimensions: ", 
                         paste(object_dim, collapse = " "),
                         "\nNew dimensions: ",
                         paste(new_dim, collapse = " "))
            object_dim <<- new_dim
            message(msg)
        }
        return(TRUE)
    }

    return(f)
}

tc <- addTaskCallback(monitor_object("mtcars"))

# When you want to remove the callback
removeTaskCallback(tc)

And an example session...

> tc <- addTaskCallback(monitor_object("mtcars"))
> mtcars <- mtcars[,-1]
mtcars changed.
Old dimensions: 32 11
New dimensions: 32 10
> head(mtcars)
                  cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710          4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive      6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant             6  225 105 2.76 3.460 20.22  1  0    3    1
> mtcars <- mtcars[1:5,]
mtcars changed.
Old dimensions: 32 10
New dimensions: 5 10

I've actually expanded upon this code because it was a fun little exercise and made it so you can specify the function you want to use to monitor the object. I made a gist for the function that can be found here.

Upvotes: 5

John Paul
John Paul

Reputation: 12654

One option would be to write a new function like so:

omitter<-function(x){
  x<-na.omit(x)
  print(paste0("The dimensions are now ",dim(x)[1]," x ", dim(x)[2]))
  return(x)
}

This would print the message and store the new object:

y<-omitter(data.frame(a=c(1:5,NA)))

> y
  a
1 1
2 2
3 3
4 4
5 5

Edit: based on @DavidKlotz comment above, you could use message in place of print

Upvotes: 0

Related Questions