34

I've been given a big chunk of poorly formatted monolithic R code with plenty of functions, and I'd like to work out what functions call what functions.

I thought I could use roxygen's @callGraph stuff, but a) the code needs to be in a package, which will be a headache with this code, and b) it doesn't even seem to work when I do run it on a simple package of mine. I see a posting from one of the Roxygen authors saying call graph generation is disabled because of the Rgraphviz dependency, but the code is there. Anyway.

Anyone got a better way of quickly working out that foo calls bar, baz, and qux, and that qux calls quux?

Edit: Solutions based on R's profiling system are great, assuming you can actually run the code... Half the stuff in the files doesn't get run, and I don't know what it does... Static analysis is too much to hope for, I guess.

Edit 2: Roxygen's call graph stuff seems to do a static analysis, based on recursive descent of the expression of the function and checking for is.callable. It would be lovely to be able to run this on any function... I may play with this tomorrow...

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
Spacedman
  • 92,590
  • 12
  • 140
  • 224
  • You might look at the 'foodweb' function from the 'mvbutils' package on CRAN. Here is a link to an article describing its usage: http://www.r-bloggers.com/r-function-of-the-day-foodweb-2/ Disclaimer: this is my website and I wrote that article. – Erik Iverson Jan 25 '11 at 18:45
  • I was pondering the idea of writing a function that would do something exactly like this. And that was right about the time when you posted the above blog post. Great minds think alike? :) – Roman Luštrik Jan 26 '11 at 10:06
  • Pretty much exactly what I was after. Now to turn the output into dot format, or maybe load it into some other R graph package... – Spacedman Jan 26 '11 at 11:05
  • See this post for a multi-language static call graph generator, which also supports R: stackoverflow.com/a/67692928/251947 – Chris Koknat Aug 24 '21 at 22:24

4 Answers4

5

Would profr help you out? From the documentation:

> ?profr
> glm_ex <- profr(example(glm))
Read 17 items
>      head(glm_ex)
             f level time start  end  leaf source
8      example     1 0.32  0.00 0.32 FALSE  utils
9  <Anonymous>     2 0.04  0.00 0.04 FALSE   <NA>
10      source     2 0.28  0.04 0.32 FALSE   base
11  prepare_Rd     3 0.02  0.00 0.02 FALSE   <NA>
12      render     3 0.02  0.02 0.04 FALSE   <NA>
13 getSrcLines     3 0.02  0.04 0.06 FALSE   base
>      summary(glm_ex)
               f          level             time          start       
 eval.with.vis  :10   Min.   : 1.000   Min.   :0.02   Min.   :0.0000  
 <Anonymous>    : 3   1st Qu.: 4.000   1st Qu.:0.02   1st Qu.:0.1200  
 lazyLoadDBfetch: 3   Median : 6.000   Median :0.02   Median :0.2000  
 %in%           : 3   Mean   : 7.211   Mean   :0.03   Mean   :0.1769  
 inherits       : 3   3rd Qu.: 9.000   3rd Qu.:0.02   3rd Qu.:0.2600  
 is.factor      : 3   Max.   :22.000   Max.   :0.32   Max.   :0.3000  
 (Other)        :65                                                   
      end            leaf            source         
 Min.   :0.0200   Mode :logical   Length:90         
 1st Qu.:0.1500   FALSE:75        Class :character  
 Median :0.2400   TRUE :15        Mode  :character  
 Mean   :0.2069   NA's :0                           
 3rd Qu.:0.2800                                     
 Max.   :0.3200                                     

> plot(glm_ex)

enter image description here

Not quite what you are after, but you may be able to adapt it to your needs.

csgillespie
  • 59,189
  • 14
  • 150
  • 185
4

The CodeDepends package (CRAN, website, GitHub) looks interesting, I haven't looked into it though. Among others, it promises

  • creating call graphs between sets of functions

presumably through the makeCallGraph() function.

krlmlr
  • 25,056
  • 14
  • 120
  • 217
2

I have not used it, but a quick look at the proftools package indicates that it can do this. You'll have to run Rprof first and then use the proftools to analyze the output. I think you want the plotProfileCallGraph() function.

JD Long
  • 59,675
  • 58
  • 202
  • 294
2

Besides proftools and profr, there is the Perl script by Romain on the R Wiki. Combined with graphviz, it does graphs (with edges weighted) -- see here for more.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725