Happy birthday, data.table

tips
tutorials
developer
Author

Toby Hocking

Published

April 15, 2026

Happy birthday, data.table!

Introduction: GitHub commits and governance

Since this is the 20th anniversary of Matt’s original CRAN submission, I wanted to do some analysis of contributors over time, to emphasize the great community that has been working to improve data.table in recent years.

To do that, a simple way is to look at the commit history on GitHub:

github data table commits

Above we can see that there is a record number of commits since late 2023, which is when the formal project governance was adopted. That was a turning point in the package’s history. Prior to that, Matt Dowle was reviewing and merging all PRs. Matt did a fantastic job ensuring quality and performance, but it was difficult to handle the sheer volume of issues and PRs, since data.table is so widely used in the R community (1700+ other CRAN packages list data.table as a hard dependency as of April 2026). Now, there is a group of several committers, including myself, that can review and merge PRs. So it is much easier to handle the increased number of contributions from the much larger group of people that are now using data.table. If you are using data.table, please consider contributing! We are an open and diverse group, and there are lots of ways to contribute:

  • writing a tutorial about data.table on your blog.
  • look at the list of open issues on GitHub, try to reproduce the issue on your machine, and add a comment to the issue that explains if the issue is still happening on your machine (or not).
  • sending us a new issue,
    • if you think there may be a problem that could be fixed.
    • if you have an important use case for which we could possibly add new tests (for validity of output, or for performance using atime).
    • anything else! We primary use issues to communicate, so please do not hesitate to open a new issue.
  • write a PR with a
    • vignette translation, currently available in en, es, fr, ru.
    • fix for an existing issue.
    • When the PR is merged, you will be invited to become a Project Member, meaning you will have permission to create new branches in the central data.table repo.
  • Volunteer to become a Reviewer for the R or C code files in data.table that are important to you! You will be notified when there is a PR with modifications, and you can comment to ask for improvements to the PR.
  • Eventually after contributing and reviewing several PRs, you can be invited to become a Committer, with permission to merge PRs into the master branch.

Now is a great time to get involved. Your contributions to data.table will be greatly appreciated by the community around data.table, including the wider community of other packages that depend on data.table for its state-of-the-art efficiency.

New analysis of CRAN authors and contributors

In the rest of this article, my goal is to analyze the number of authors and contributors in CRAN releases of data.table. We first download data on all releases, using code from my previous post about the release history of data.table.

Download Archive web page

We can download the Archive web page for data.table via the code below. Note that In R ≥ 4.5, tools::CRAN_archive_db() can be used, but here we use download.file() to give an example of how HTML can be parsed into a data table using library(nc), one of my packages with strong data.table integration, which was recently granted a Seal of Approval!

Archive <- "https://cloud.r-project.org/src/contrib/Archive/"
get_Archive <- function(Package, releases.dir="~/releases"){
  dir.create(releases.dir, showWarnings = FALSE)
  pkg.html <- file.path(releases.dir, paste0(Package, ".html"))
  if(!file.exists(pkg.html)){
    u <- paste0(Archive, Package)
    download.file(u, pkg.html)
  }
  readLines(pkg.html)
}
(Archive.data.table <- get_Archive("data.table"))
 [1] "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"                                                                                                                                                
 [2] "<html>"                                                                                                                                                                                                                                        
 [3] " <head>"                                                                                                                                                                                                                                       
 [4] "  <title>Index of /src/contrib/Archive/data.table</title>"                                                                                                                                                                                     
 [5] " </head>"                                                                                                                                                                                                                                      
 [6] " <body>"                                                                                                                                                                                                                                       
 [7] "<h1>Index of /src/contrib/Archive/data.table</h1>"                                                                                                                                                                                             
 [8] "<pre>      <a href=\"?C=N;O=D\">Name</a>                       <a href=\"?C=M;O=A\">Last modified</a>      <a href=\"?C=S;O=A\">Size</a>  <hr>      <a href=\"/src/contrib/Archive/\">Parent Directory</a>                                -   "
 [9] "      <a href=\"data.table_1.0.tar.gz\">data.table_1.0.tar.gz</a>      2006-04-14 22:03   16K  "                                                                                                                                               
[10] "      <a href=\"data.table_1.1.tar.gz\">data.table_1.1.tar.gz</a>      2008-08-27 07:35   40K  "                                                                                                                                               
[11] "      <a href=\"data.table_1.10.0.tar.gz\">data.table_1.10.0.tar.gz</a>   2016-12-03 10:05  2.9M  "                                                                                                                                            
[12] "      <a href=\"data.table_1.10.2.tar.gz\">data.table_1.10.2.tar.gz</a>   2017-01-31 15:09  2.9M  "                                                                                                                                            
[13] "      <a href=\"data.table_1.10.4-1.tar.gz\">data.table_1.10.4-1.tar.gz</a> 2017-10-09 22:36  2.9M  "                                                                                                                                          
[14] "      <a href=\"data.table_1.10.4-2.tar.gz\">data.table_1.10.4-2.tar.gz</a> 2017-10-12 14:03  2.9M  "                                                                                                                                          
[15] "      <a href=\"data.table_1.10.4-3.tar.gz\">data.table_1.10.4-3.tar.gz</a> 2017-10-27 07:40  2.9M  "                                                                                                                                          
[16] "      <a href=\"data.table_1.10.4.tar.gz\">data.table_1.10.4.tar.gz</a>   2017-02-01 14:52  2.9M  "                                                                                                                                            
[17] "      <a href=\"data.table_1.11.0.tar.gz\">data.table_1.11.0.tar.gz</a>   2018-05-01 17:00  3.1M  "                                                                                                                                            
[18] "      <a href=\"data.table_1.11.2.tar.gz\">data.table_1.11.2.tar.gz</a>   2018-05-08 16:16  3.1M  "                                                                                                                                            
[19] "      <a href=\"data.table_1.11.4.tar.gz\">data.table_1.11.4.tar.gz</a>   2018-05-27 16:34  3.1M  "                                                                                                                                            
[20] "      <a href=\"data.table_1.11.6.tar.gz\">data.table_1.11.6.tar.gz</a>   2018-09-19 22:10  3.2M  "                                                                                                                                            
[21] "      <a href=\"data.table_1.11.8.tar.gz\">data.table_1.11.8.tar.gz</a>   2018-09-30 13:30  3.1M  "                                                                                                                                            
[22] "      <a href=\"data.table_1.12.0.tar.gz\">data.table_1.12.0.tar.gz</a>   2019-01-13 11:50  3.2M  "                                                                                                                                            
[23] "      <a href=\"data.table_1.12.2.tar.gz\">data.table_1.12.2.tar.gz</a>   2019-04-07 10:06  3.2M  "                                                                                                                                            
[24] "      <a href=\"data.table_1.12.4.tar.gz\">data.table_1.12.4.tar.gz</a>   2019-10-03 09:10  4.8M  "                                                                                                                                            
[25] "      <a href=\"data.table_1.12.6.tar.gz\">data.table_1.12.6.tar.gz</a>   2019-10-18 22:20  4.7M  "                                                                                                                                            
[26] "      <a href=\"data.table_1.12.8.tar.gz\">data.table_1.12.8.tar.gz</a>   2019-12-09 10:30  4.7M  "                                                                                                                                            
[27] "      <a href=\"data.table_1.13.0.tar.gz\">data.table_1.13.0.tar.gz</a>   2020-07-24 09:40  5.0M  "                                                                                                                                            
[28] "      <a href=\"data.table_1.13.2.tar.gz\">data.table_1.13.2.tar.gz</a>   2020-10-19 18:50  5.0M  "                                                                                                                                            
[29] "      <a href=\"data.table_1.13.4.tar.gz\">data.table_1.13.4.tar.gz</a>   2020-12-08 10:10  5.0M  "                                                                                                                                            
[30] "      <a href=\"data.table_1.13.6.tar.gz\">data.table_1.13.6.tar.gz</a>   2020-12-30 15:50  5.1M  "                                                                                                                                            
[31] "      <a href=\"data.table_1.14.0.tar.gz\">data.table_1.14.0.tar.gz</a>   2021-02-21 06:00  5.1M  "                                                                                                                                            
[32] "      <a href=\"data.table_1.14.10.tar.gz\">data.table_1.14.10.tar.gz</a>  2023-12-08 11:20  5.1M  "                                                                                                                                           
[33] "      <a href=\"data.table_1.14.2.tar.gz\">data.table_1.14.2.tar.gz</a>   2021-09-27 16:30  5.1M  "                                                                                                                                            
[34] "      <a href=\"data.table_1.14.4.tar.gz\">data.table_1.14.4.tar.gz</a>   2022-10-17 10:32  5.1M  "                                                                                                                                            
[35] "      <a href=\"data.table_1.14.6.tar.gz\">data.table_1.14.6.tar.gz</a>   2022-11-16 21:30  5.0M  "                                                                                                                                            
[36] "      <a href=\"data.table_1.14.8.tar.gz\">data.table_1.14.8.tar.gz</a>   2023-02-17 12:20  5.1M  "                                                                                                                                            
[37] "      <a href=\"data.table_1.15.0.tar.gz\">data.table_1.15.0.tar.gz</a>   2024-01-30 07:40  5.1M  "                                                                                                                                            
[38] "      <a href=\"data.table_1.15.2.tar.gz\">data.table_1.15.2.tar.gz</a>   2024-02-29 07:10  5.1M  "                                                                                                                                            
[39] "      <a href=\"data.table_1.15.4.tar.gz\">data.table_1.15.4.tar.gz</a>   2024-03-30 23:50  5.1M  "                                                                                                                                            
[40] "      <a href=\"data.table_1.16.0.tar.gz\">data.table_1.16.0.tar.gz</a>   2024-08-27 04:20  5.1M  "                                                                                                                                            
[41] "      <a href=\"data.table_1.16.2.tar.gz\">data.table_1.16.2.tar.gz</a>   2024-10-10 16:10  5.2M  "                                                                                                                                            
[42] "      <a href=\"data.table_1.16.4.tar.gz\">data.table_1.16.4.tar.gz</a>   2024-12-06 15:10  5.2M  "                                                                                                                                            
[43] "      <a href=\"data.table_1.17.0.tar.gz\">data.table_1.17.0.tar.gz</a>   2025-02-22 06:10  5.6M  "                                                                                                                                            
[44] "      <a href=\"data.table_1.17.2.tar.gz\">data.table_1.17.2.tar.gz</a>   2025-05-12 11:10  5.6M  "                                                                                                                                            
[45] "      <a href=\"data.table_1.17.4.tar.gz\">data.table_1.17.4.tar.gz</a>   2025-05-26 12:40  5.6M  "                                                                                                                                            
[46] "      <a href=\"data.table_1.17.6.tar.gz\">data.table_1.17.6.tar.gz</a>   2025-06-17 03:40  5.6M  "                                                                                                                                            
[47] "      <a href=\"data.table_1.17.8.tar.gz\">data.table_1.17.8.tar.gz</a>   2025-07-10 10:30  5.5M  "                                                                                                                                            
[48] "      <a href=\"data.table_1.18.0.tar.gz\">data.table_1.18.0.tar.gz</a>   2025-12-24 12:05  5.7M  "                                                                                                                                            
[49] "      <a href=\"data.table_1.18.2.1.tar.gz\">data.table_1.18.2.1.tar.gz</a> 2026-01-27 11:40  5.7M  "                                                                                                                                          
[50] "      <a href=\"data.table_1.2.tar.gz\">data.table_1.2.tar.gz</a>      2008-09-01 06:59   40K  "                                                                                                                                               
[51] "      <a href=\"data.table_1.4.1.tar.gz\">data.table_1.4.1.tar.gz</a>    2010-05-03 08:40  344K  "                                                                                                                                             
[52] "      <a href=\"data.table_1.5.1.tar.gz\">data.table_1.5.1.tar.gz</a>    2011-01-08 08:31  589K  "                                                                                                                                             
[53] "      <a href=\"data.table_1.5.2.tar.gz\">data.table_1.5.2.tar.gz</a>    2011-01-21 09:03  607K  "                                                                                                                                             
[54] "      <a href=\"data.table_1.5.3.tar.gz\">data.table_1.5.3.tar.gz</a>    2011-02-11 08:49  623K  "                                                                                                                                             
[55] "      <a href=\"data.table_1.5.tar.gz\">data.table_1.5.tar.gz</a>      2010-09-14 06:23  589K  "                                                                                                                                               
[56] "      <a href=\"data.table_1.6.1.tar.gz\">data.table_1.6.1.tar.gz</a>    2011-06-29 09:41  692K  "                                                                                                                                             
[57] "      <a href=\"data.table_1.6.2.tar.gz\">data.table_1.6.2.tar.gz</a>    2011-07-02 14:21  693K  "                                                                                                                                             
[58] "      <a href=\"data.table_1.6.3.tar.gz\">data.table_1.6.3.tar.gz</a>    2011-08-04 11:28  698K  "                                                                                                                                             
[59] "      <a href=\"data.table_1.6.4.tar.gz\">data.table_1.6.4.tar.gz</a>    2011-08-10 05:50  705K  "                                                                                                                                             
[60] "      <a href=\"data.table_1.6.5.tar.gz\">data.table_1.6.5.tar.gz</a>    2011-08-25 04:35  711K  "                                                                                                                                             
[61] "      <a href=\"data.table_1.6.6.tar.gz\">data.table_1.6.6.tar.gz</a>    2011-08-25 20:08  712K  "                                                                                                                                             
[62] "      <a href=\"data.table_1.6.tar.gz\">data.table_1.6.tar.gz</a>      2011-04-24 06:07  684K  "                                                                                                                                               
[63] "      <a href=\"data.table_1.7.1.tar.gz\">data.table_1.7.1.tar.gz</a>    2011-10-22 12:05  728K  "                                                                                                                                             
[64] "      <a href=\"data.table_1.7.10.tar.gz\">data.table_1.7.10.tar.gz</a>   2012-02-07 08:43  758K  "                                                                                                                                            
[65] "      <a href=\"data.table_1.7.2.tar.gz\">data.table_1.7.2.tar.gz</a>    2011-11-07 14:05  735K  "                                                                                                                                             
[66] "      <a href=\"data.table_1.7.3.tar.gz\">data.table_1.7.3.tar.gz</a>    2011-11-25 07:12  741K  "                                                                                                                                             
[67] "      <a href=\"data.table_1.7.4.tar.gz\">data.table_1.7.4.tar.gz</a>    2011-11-29 06:57  741K  "                                                                                                                                             
[68] "      <a href=\"data.table_1.7.5.tar.gz\">data.table_1.7.5.tar.gz</a>    2011-12-04 12:51  742K  "                                                                                                                                             
[69] "      <a href=\"data.table_1.7.6.tar.gz\">data.table_1.7.6.tar.gz</a>    2011-12-13 08:36  743K  "                                                                                                                                             
[70] "      <a href=\"data.table_1.7.7.tar.gz\">data.table_1.7.7.tar.gz</a>    2011-12-15 10:07  744K  "                                                                                                                                             
[71] "      <a href=\"data.table_1.7.8.tar.gz\">data.table_1.7.8.tar.gz</a>    2012-01-25 07:53  754K  "                                                                                                                                             
[72] "      <a href=\"data.table_1.7.9.tar.gz\">data.table_1.7.9.tar.gz</a>    2012-01-31 07:30  756K  "                                                                                                                                             
[73] "      <a href=\"data.table_1.8.0.tar.gz\">data.table_1.8.0.tar.gz</a>    2012-07-16 08:21  768K  "                                                                                                                                             
[74] "      <a href=\"data.table_1.8.10.tar.gz\">data.table_1.8.10.tar.gz</a>   2013-09-03 04:41  914K  "                                                                                                                                            
[75] "      <a href=\"data.table_1.8.2.tar.gz\">data.table_1.8.2.tar.gz</a>    2012-07-17 19:51  799K  "                                                                                                                                             
[76] "      <a href=\"data.table_1.8.4.tar.gz\">data.table_1.8.4.tar.gz</a>    2012-11-09 15:23  820K  "                                                                                                                                             
[77] "      <a href=\"data.table_1.8.6.tar.gz\">data.table_1.8.6.tar.gz</a>    2012-11-13 13:28  821K  "                                                                                                                                             
[78] "      <a href=\"data.table_1.8.8.tar.gz\">data.table_1.8.8.tar.gz</a>    2013-03-06 06:31  874K  "                                                                                                                                             
[79] "      <a href=\"data.table_1.9.2.tar.gz\">data.table_1.9.2.tar.gz</a>    2014-02-27 13:49  1.0M  "                                                                                                                                             
[80] "      <a href=\"data.table_1.9.4.tar.gz\">data.table_1.9.4.tar.gz</a>    2014-10-02 06:41  927K  "                                                                                                                                             
[81] "      <a href=\"data.table_1.9.6.tar.gz\">data.table_1.9.6.tar.gz</a>    2015-09-19 20:13  3.5M  "                                                                                                                                             
[82] "      <a href=\"data.table_1.9.8.tar.gz\">data.table_1.9.8.tar.gz</a>    2016-11-25 11:55  2.9M  "                                                                                                                                             
[83] "<hr></pre>"                                                                                                                                                                                                                                    
[84] "<address>Apache/2.4.66 (Unix) Server at cloud.r-project.org Port 80</address>"                                                                                                                                                                 
[85] "</body></html>"                                                                                                                                                                                                                                

The output above shows that the Archive web page has a regular structure, which we can convert into a data table using the regular expression pattern below.

file.pattern <- list(
  '(?<=>)',
  package=".*?",
  "_",
  version="[0-9.-]+",
  "[.]tar[.]gz")

The code above specifies a regular expression:

  • '(?<=>)' is a lookbehind assertion. It means to start by looking for a greater than sign, but not including that character in the match.
  • package=".*?" means to match zero or more of anything except newline (non-greedy, as few as possible), and output the match in the package column,
  • "_" means to match an underscore,
  • version="[0-9.-]+" means to match one or more digits/dots/dashes, and output them in the version column,
  • "[.]tar[.]gz" means to match the .tar.gz file name suffix.

Below we use that pattern to convert the web page into a data table with two columns,

options(datatable.print.nrows=20) # instead of default 100.
nc::capture_all_str(Archive.data.table, file.pattern)
       package  version
        <char>   <char>
 1: data.table      1.0
 2: data.table      1.1
 3: data.table   1.10.0
 4: data.table   1.10.2
 5: data.table 1.10.4-1
---                    
70: data.table    1.8.8
71: data.table    1.9.2
72: data.table    1.9.4
73: data.table    1.9.6
74: data.table    1.9.8

Next, we add to the pattern to match the release date,

library(data.table)

Attaching package: 'data.table'
The following object is masked from 'package:base':

    %notin%
Archive.pattern <- list(
  file=file.pattern,
  "</a>",
  "\\s+",
  IDate=".*?", as.IDate,
  "\\s")

The code above has

  • file=file.pattern which means to apply the previous regex, and put the matching text in the file column,
  • "</a>" which matches the closing </a> tag
  • "\\s+" which matches one or more white space characters,
  • IDate=".*?", as.IDate, which matches zero or more characters (non-greedy, as few as possible), then uses as.IDate to convert the text to efficient integer date, saved in the IDate column,
  • "\\s" means to match one white space character.

The end result is a table with one row for each matched package version, and one column for each of the named arguments:

(Archive.dt <- nc::capture_all_str(Archive.data.table, Archive.pattern))
                          file    package  version      IDate
                        <char>     <char>   <char>     <IDat>
 1:      data.table_1.0.tar.gz data.table      1.0 2006-04-14
 2:      data.table_1.1.tar.gz data.table      1.1 2008-08-27
 3:   data.table_1.10.0.tar.gz data.table   1.10.0 2016-12-03
 4:   data.table_1.10.2.tar.gz data.table   1.10.2 2017-01-31
 5: data.table_1.10.4-1.tar.gz data.table 1.10.4-1 2017-10-09
---                                                          
70:    data.table_1.8.8.tar.gz data.table    1.8.8 2013-03-06
71:    data.table_1.9.2.tar.gz data.table    1.9.2 2014-02-27
72:    data.table_1.9.4.tar.gz data.table    1.9.4 2014-10-02
73:    data.table_1.9.6.tar.gz data.table    1.9.6 2015-09-19
74:    data.table_1.9.8.tar.gz data.table    1.9.8 2016-11-25

Above the table shows all matches, in the same order as the original Archive web page. Below we key the table by date, which sorts the data in place (without allocating any new memory), and enables fast joins.

setkey(Archive.dt, IDate)
Archive.dt
Key: <IDate>
                          file    package  version      IDate
                        <char>     <char>   <char>     <IDat>
 1:      data.table_1.0.tar.gz data.table      1.0 2006-04-14
 2:      data.table_1.1.tar.gz data.table      1.1 2008-08-27
 3:      data.table_1.2.tar.gz data.table      1.2 2008-09-01
 4:    data.table_1.4.1.tar.gz data.table    1.4.1 2010-05-03
 5:      data.table_1.5.tar.gz data.table      1.5 2010-09-14
---                                                          
70:   data.table_1.17.4.tar.gz data.table   1.17.4 2025-05-26
71:   data.table_1.17.6.tar.gz data.table   1.17.6 2025-06-17
72:   data.table_1.17.8.tar.gz data.table   1.17.8 2025-07-10
73:   data.table_1.18.0.tar.gz data.table   1.18.0 2025-12-24
74: data.table_1.18.2.1.tar.gz data.table 1.18.2.1 2026-01-27

We see the table above has been sorted by release date. Next, we define a grid of dates which we will search for the nearest release.

every.year.since.2016 <- seq(
  as.IDate("2016-04-14"),
  Sys.time(),
  by="year")
(grid.dt <- setkey(data.table(
  grid.IDate=c(
    as.IDate("2006-04-14"), # first release.
    as.IDate("2011-04-14"), # fifth anniversary.
    every.year.since.2016))))
Key: <grid.IDate>
    grid.IDate
        <IDat>
 1: 2006-04-14
 2: 2011-04-14
 3: 2016-04-14
 4: 2017-04-14
 5: 2018-04-14
 6: 2019-04-14
 7: 2020-04-14
 8: 2021-04-14
 9: 2022-04-14
10: 2023-04-14
11: 2024-04-14
12: 2025-04-14
13: 2026-04-14

The code above sets the key of the grid, which sorts and enables fast joins. No variables were specified to setkey(); the default is to use all columns, in this case just one. Note that setkey() sets the key by reference, then returns the table.

Next, we do a rolling join to find which releases are nearest to each date in the grid.

(nearest.dt <- unique(Archive.dt[grid.dt, .(
  file, version, package, release=x.IDate
), roll="nearest"]))
                          file  version    package    release
                        <char>   <char>     <char>     <IDat>
 1:      data.table_1.0.tar.gz      1.0 data.table 2006-04-14
 2:      data.table_1.6.tar.gz      1.6 data.table 2011-04-24
 3:    data.table_1.9.6.tar.gz    1.9.6 data.table 2015-09-19
 4:   data.table_1.10.4.tar.gz   1.10.4 data.table 2017-02-01
 5:   data.table_1.11.0.tar.gz   1.11.0 data.table 2018-05-01
 6:   data.table_1.12.2.tar.gz   1.12.2 data.table 2019-04-07
 7:   data.table_1.13.0.tar.gz   1.13.0 data.table 2020-07-24
 8:   data.table_1.14.0.tar.gz   1.14.0 data.table 2021-02-21
 9:   data.table_1.14.4.tar.gz   1.14.4 data.table 2022-10-17
10:   data.table_1.14.8.tar.gz   1.14.8 data.table 2023-02-17
11:   data.table_1.15.4.tar.gz   1.15.4 data.table 2024-03-30
12:   data.table_1.17.2.tar.gz   1.17.2 data.table 2025-05-12
13: data.table_1.18.2.1.tar.gz 1.18.2.1 data.table 2026-01-27

The output above shows one row per release we will analyze. For each release, we download the package sources from the Archive, and extract the Author field of DESCRIPTION.

desc.dt <- nearest.dt[, {
  cache.dir <- "~/Archive"
  dir.create(cache.dir, showWarnings = FALSE)
  dt.tar.gz <- file.path(cache.dir, file)
  if(!file.exists(dt.tar.gz)){
    url.tar.gz <- paste0(Archive, package, "/", file)
    download.file(url.tar.gz, dt.tar.gz)
  }
  conn <- gzfile(dt.tar.gz, "b")
  DESCRIPTION <- file.path(package, "DESCRIPTION")
  untar(conn, files=DESCRIPTION)
  close(conn)
  as.data.table(read.dcf(DESCRIPTION)[,"Author",drop=FALSE])
}, by=.(version, release)]

To avoid printing the full Author column (a long string), we can set an option:

options(
  datatable.prettyprint.char=30, # print ... after this many characters.
  width=100) # max characters before wrapping columns to next line.
desc.dt
     version    release                              Author
      <char>     <IDat>                              <char>
 1:      1.0 2006-04-14                          Matt Dowle
 2:      1.6 2011-04-24   Matthew Dowle with many contri...
 3:    1.9.6 2015-09-19   M Dowle, A Srinivasan, T Short...
 4:   1.10.4 2017-02-01 Matt Dowle [aut, cre],\nArun Sri...
 5:   1.11.0 2018-05-01 Matt Dowle [aut, cre],\nArun Sri...
 6:   1.12.2 2019-04-07 Matt Dowle [aut, cre],\nArun Sri...
 7:   1.13.0 2020-07-24 Matt Dowle [aut, cre],\nArun Sri...
 8:   1.14.0 2021-02-21 Matt Dowle [aut, cre],\nArun Sri...
 9:   1.14.4 2022-10-17 Matt Dowle [aut, cre],\nArun Sri...
10:   1.14.8 2023-02-17 Matt Dowle [aut, cre],\nArun Sri...
11:   1.15.4 2024-03-30 Tyson Barrett [aut, cre],\nMatt ...
12:   1.17.2 2025-05-12   Tyson Barrett [aut, cre] (ORCI...
13: 1.18.2.1 2026-01-27   Tyson Barrett [aut, cre] (ORCI...

We see above that the Author field can contain newlines (after the comma), which we remove below, to make later parsing easier:

desc.dt[, no.newlines := gsub("\n", " ", Author)][]
     version    release                              Author                       no.newlines
      <char>     <IDat>                              <char>                            <char>
 1:      1.0 2006-04-14                          Matt Dowle                        Matt Dowle
 2:      1.6 2011-04-24   Matthew Dowle with many contri... Matthew Dowle with many contri...
 3:    1.9.6 2015-09-19   M Dowle, A Srinivasan, T Short... M Dowle, A Srinivasan, T Short...
 4:   1.10.4 2017-02-01 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
 5:   1.11.0 2018-05-01 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
 6:   1.12.2 2019-04-07 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
 7:   1.13.0 2020-07-24 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
 8:   1.14.0 2021-02-21 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
 9:   1.14.4 2022-10-17 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
10:   1.14.8 2023-02-17 Matt Dowle [aut, cre],\nArun Sri... Matt Dowle [aut, cre], Arun Sr...
11:   1.15.4 2024-03-30 Tyson Barrett [aut, cre],\nMatt ... Tyson Barrett [aut, cre], Matt...
12:   1.17.2 2025-05-12   Tyson Barrett [aut, cre] (ORCI... Tyson Barrett [aut, cre] (ORCI...
13: 1.18.2.1 2026-01-27   Tyson Barrett [aut, cre] (ORCI... Tyson Barrett [aut, cre] (ORCI...

The output above has a new column of comma-separated authors per release (with no newlines). We would like to convert these data to a table with one year per author. A simple approach would be

head(sapply(strsplit(desc.dt$no.newlines, ", "), head))
[[1]]
[1] "Matt Dowle"

[[2]]
[1] "Matthew Dowle with many contributions from Tom Short.  See SVN logs on R-Forge."

[[3]]
[1] "M Dowle"                                       "A Srinivasan"                                 
[3] "T Short"                                       "S Lianoglou with contributions from R Saporta"
[5] "E Antonyan"                                   

[[4]]
[1] "Matt Dowle [aut"       "cre]"                  "Arun Srinivasan [aut]" "Jan Gorecki [ctb]"    
[5] "Tom Short [ctb]"       "Steve Lianoglou [ctb]"

[[5]]
[1] "Matt Dowle [aut"       "cre]"                  "Arun Srinivasan [aut]" "Jan Gorecki [ctb]"    
[5] "Michael Chirico [ctb]" "Pasha Stetsenko [ctb]"

[[6]]
[1] "Matt Dowle [aut"       "cre]"                  "Arun Srinivasan [aut]" "Jan Gorecki [ctb]"    
[5] "Michael Chirico [ctb]" "Pasha Stetsenko [ctb]"

It is clear that the result above does not quite work (Matt’s aut, cre role contains a comma so is broken into two entries). Instead we can use the regular expression below.

author.pattern <- list(
  name=".+?",
  nc::quantifier(
    " \\[",
    roles=".+?",
    "\\]",
    "?"),
  nc::quantifier(
    " \\(", 
    paren=".+?",
    "\\)",
    "?"),
  ## each author ends with one of these (\z means end of string).
  nc::alternatives(" with (?:many )?contributions from ", ", ", "\\z"))
(author.dt <- desc.dt[, nc::capture_all_str(
  no.newlines, author.pattern
), by=.(version, release)])
      version    release                              name  roles       paren
       <char>     <IDat>                            <char> <char>      <char>
  1:      1.0 2006-04-14                        Matt Dowle                   
  2:      1.6 2011-04-24                     Matthew Dowle                   
  3:      1.6 2011-04-24 Tom Short.  See SVN logs on R-...                   
  4:    1.9.6 2015-09-19                           M Dowle                   
  5:    1.9.6 2015-09-19                      A Srinivasan                   
 ---                                                                         
551: 1.18.2.1 2026-01-27                        @badasahog    ctb GitHub user
552: 1.18.2.1 2026-01-27                      Vinit Thakur    ctb            
553: 1.18.2.1 2026-01-27                       Mukul Kumar    ctb            
554: 1.18.2.1 2026-01-27                    Ildikó Czeller    ctb            
555: 1.18.2.1 2026-01-27                       Manmita Das    ctb            

The table above has one row for each time a person appears in the Author field of one of the releases. We will analyze the roles.

author.dt[roles==""]
   version    release                              name  roles  paren
    <char>     <IDat>                            <char> <char> <char>
1:     1.0 2006-04-14                        Matt Dowle              
2:     1.6 2011-04-24                     Matthew Dowle              
3:     1.6 2011-04-24 Tom Short.  See SVN logs on R-...              
4:   1.9.6 2015-09-19                           M Dowle              
5:   1.9.6 2015-09-19                      A Srinivasan              
6:   1.9.6 2015-09-19                           T Short              
7:   1.9.6 2015-09-19                       S Lianoglou              
8:   1.9.6 2015-09-19                         R Saporta              
9:   1.9.6 2015-09-19                        E Antonyan              

We see some old entries above with missing roles, which we fill in below.

linewidth.values <- c(
  ctb=2,
  aut=1)
author.dt[
, Role := factor(fcase(
  roles=="aut, cre" | grepl("Dowle|Srinivasan", name), "aut",
  roles=="", "ctb",
  default=roles), names(linewidth.values))
][
, table(roles, Role, useNA="always")
]
          Role
roles      ctb aut <NA>
             5   4    0
  aut        0  26    0
  aut, cre   0  10    0
  ctb      510   0    0
  <NA>       0   0    0

Above we use fcase() to create a new Role column, with factor levels in a non-default order (to control legend entry display order below). Then we chain square brackets to display a table which shows how roles are mapped to Role. The counts look reasonable, so the next step is to count how many people with each role in each release:

(count.dt <- author.dt[, .(people=.N), by=.(release, version, Role)])
       release  version   Role people
        <IDat>   <char> <fctr>  <int>
 1: 2006-04-14      1.0    aut      1
 2: 2011-04-24      1.6    aut      1
 3: 2011-04-24      1.6    ctb      1
 4: 2015-09-19    1.9.6    aut      2
 5: 2015-09-19    1.9.6    ctb      4
---                                  
21: 2024-03-30   1.15.4    ctb     65
22: 2025-05-12   1.17.2    aut      8
23: 2025-05-12   1.17.2    ctb     81
24: 2026-01-27 1.18.2.1    aut      8
25: 2026-01-27 1.18.2.1    ctb     87

How has this evolved in the past ten years?

library(ggplot2)
(gg <- ggplot(count.dt, aes(
  release, people, color=Role))+
   ggtitle("data.table contributor and author counts for selected releases")+
   theme(
     panel.grid.minor=element_blank(),
     axis.text.x=element_text(hjust=1, angle=40))+
   geom_line(aes(linewidth=Role))+
   geom_point(shape=21, fill="white")+
   scale_x_date(breaks=grid.dt$grid.IDate)+
   scale_linewidth_manual(values=linewidth.values)+
   scale_y_log10(limits=c(0.2, 500)))

Above we see a time series showing the increasing authors and contributors over time. To emphasize the values at each release, we add direct labels below:

geom_dl_poly <- function(role, position, direction)directlabels::geom_dl(aes(
  label=sprintf("%s\n%s %s", version, people, Role)),
  data=count.dt[Role==role],
  method=list(
    cex=0.7, # text size of direct labels.
    directlabels::dl.add(y=direction*0.2),# cm between polygon point and data point.
    directlabels::polygon.method(
      position, offset.cm=0.5))) #space between polygon point and text.
(dl <- gg+
  geom_dl_poly("ctb", "top", 1)+
  geom_dl_poly("aut", "bottom", -1))

The figure above shows that the number of authors and contributors has greatly expanded in the second decade of data.table. I would especially like to thank the other new Committers since adoption of the formal governance. Jan Gorecki, Mike Chirico, Ben Schwendinger, Tyson Barrett, and Ivan Krylov have been doing most of the work, writing, reviewing and merging PRs, that is keeping data.table a relevant and state-of-the-art package for data science. Another special thank you goes out to Kelly Bodwin, who lead the effort to get data.table accepted as a NumFOCUS affiliated project (since 2025). I’m looking forward to the third decade!

Conclusion

We have shown how to download CRAN package release data, how to parse the web pages using the nc package and regular expressions, how to summarize/analyze using data.table, and how to visualize using ggplot2. If you are using data.table, please consider contributing! We are an open and inclusive group that appreciates new contributions!