dataTable.R

Plain text source: dataTable.R


# -*- Mode:R; Coding:utf-8; fill-column:160 -*-

################################################################################################################################################################
# @file      dataTable.R
# @author    Mitch Richling <https://www.mitchr.me>
# @Copyright Copyright 2015 by Mitch Richling.  All rights reserved.
# @brief     Some data.table basics.@EOL
# @Keywords  r package cran data.table dataTable
#
# Some of the basics of data.table.  Note that dplyr works with data.table.  So if you have been using dplyr with data.frames, you can get a significant boost
# in performance by simply dropping in data.table -- no code changes required!
#

################################################################################################################################################################
# We will be working with the following data

mtcars
                     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
################################################################################################################################################################
# One can directly coerce a data.frame into a data.table; however, row names will be lost.  We can do something like this to keep them:

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

mtcarsDT[2,]
   mpg cyl disp  hp drat    wt  qsec vs am gear carb         names
1:  21   6  160 110  3.9 2.875 17.02  0  1    4    4 Mazda RX4 Wag
################################################################################################################################################################
# Note that when we only have one index, we don't need the comma!!

mtcarsDT[2]
   mpg cyl disp  hp drat    wt  qsec vs am gear carb         names
1:  21   6  160 110  3.9 2.875 17.02  0  1    4    4 Mazda RX4 Wag
################################################################################################################################################################
# Get rows 2 through 5

mtcarsDT[2:5]
    mpg cyl disp  hp drat    wt  qsec vs am gear carb             names
1: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4     Mazda RX4 Wag
2: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1        Datsun 710
3: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1    Hornet 4 Drive
4: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 Hornet Sportabout
################################################################################################################################################################
# Get rows where cyl is equal to 6

mtcarsDT[cyl==6]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb          names
1: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4      Mazda RX4
2: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  Mazda RX4 Wag
3: 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1 Hornet 4 Drive
4: 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1        Valiant
5: 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4       Merc 280
6: 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4      Merc 280C
7: 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6   Ferrari Dino
################################################################################################################################################################
# Grab column "cyl" the data.frame way.

mtcarsDT$cyl
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
################################################################################################################################################################
# The second index of a data.table is an expression on the rows selected by the first index.  In this example, the expression is "cyl".  As this will evaluate
# to the cyl column, that is what is returned.

mtcarsDT[,cyl]
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
################################################################################################################################################################
# When the second index is a list, then a new data.table will be returned instead of a simple vector. For example we can extract a new data.table with three
# columns (two of which we rename)

mtcarsDT[,list(cylinder=cyl, weight=wt, gear)]
    cylinder weight gear
 1:        6  2.620    4
 2:        6  2.875    4
 3:        4  2.320    4
 4:        6  3.215    3
 5:        8  3.440    3
 6:        6  3.460    3
 7:        8  3.570    3
 8:        4  3.190    4
 9:        4  3.150    4
10:        6  3.440    4
11:        6  3.440    4
12:        8  4.070    3
13:        8  3.730    3
14:        8  3.780    3
15:        8  5.250    3
16:        8  5.424    3
17:        8  5.345    3
18:        4  2.200    4
19:        4  1.615    4
20:        4  1.835    4
21:        4  2.465    3
22:        8  3.520    3
23:        8  3.435    3
24:        8  3.840    3
25:        8  3.845    3
26:        4  1.935    4
27:        4  2.140    5
28:        4  1.513    5
29:        8  3.170    5
30:        6  2.770    5
31:        8  3.570    5
32:        4  2.780    4
    cylinder weight gear
################################################################################################################################################################
# The result of the computation in the second index can be stuffed back into the data.table.

mtcarsDT[,cylTimesGear:=cyl*gear]

################################################################################################################################################################
# That second expression can do more than transform columns into new columns; it aggregate things.  This is roughly equivalent to: with(mtcars, sum(wt))

mtcarsDT[,sum(wt)]
[1] 102.952
################################################################################################################################################################
# As before, if the second expression is a list we get a new data.table.  If the second expression computes aggregates, then we get more than one
# aggregation. This is roughly equivalent to: with(mtcars, list(daSum=sum(wt), daSd=sd(wt)))

mtcarsDT[,list(daSum=sum(wt), daSd=sd(wt))]
     daSum      daSd
1: 102.952 0.9784574
################################################################################################################################################################
# We can aggregate by groups too.  This is roughly equivalent to: tapply(mtcars$wt, mtcars$cyl, sum)

mtcarsDT[,sum(wt),cyl]
   cyl     V1
1:   6 21.820
2:   4 25.143
3:   8 55.989
################################################################################################################################################################
# The groups can contain more than one factor if we use a list.  We can also name the aggregate column if we put that in a list.

mtcarsDT[,list(sumWt=sum(wt)),list(cyl, gear)]
   cyl gear  sumWt
1:   6    4 12.375
2:   4    4 19.025
3:   6    3  6.675
4:   8    3 49.249
5:   4    3  2.465
6:   4    5  3.653
7:   8    5  6.740
8:   6    5  2.770
################################################################################################################################################################
# We can aggregate by groups and put it back into the data.table!!

mtcarsDT[,sumWtByCyl := sum(wt),cyl]

################################################################################################################################################################
# If you have a "key column" set, you can index with strings -- note that this will change the order of the table to store on the key.

setkey(mtcarsDT,names)
mtcarsDT["Valiant"]
    mpg cyl disp  hp drat   wt  qsec vs am gear carb   names cylTimesGear sumWtByCyl
1: 18.1   6  225 105 2.76 3.46 20.22  1  0    3    1 Valiant           18      21.82
################################################################################################################################################################
# The values in the "key column" need not be unique:

mtcarsDT$cylNames <- paste("cyl", mtcarsDT$cyl, sep='=')
setkey(mtcarsDT,cylNames)
mtcarsDT["cyl=6"]
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb          names cylTimesGear sumWtByCyl cylNames
1: 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6   Ferrari Dino           30      21.82    cyl=6
2: 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1 Hornet 4 Drive           18      21.82    cyl=6
3: 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4      Mazda RX4           24      21.82    cyl=6
4: 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  Mazda RX4 Wag           24      21.82    cyl=6
5: 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4       Merc 280           24      21.82    cyl=6
6: 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4      Merc 280C           24      21.82    cyl=6
7: 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1        Valiant           18      21.82    cyl=6
################################################################################################################################################################
# You can get just the first result from a key column lockup like so (get the last with "last")

mtcarsDT["cyl=6",mult="first"]
    mpg cyl disp  hp drat   wt qsec vs am gear carb        names cylTimesGear sumWtByCyl cylNames
1: 19.7   6  145 175 3.62 2.77 15.5  0  1    5    6 Ferrari Dino           30      21.82    cyl=6
################################################################################################################################################################
# With things sorted on cyl, things like cumulative sums become interesting -- especially when added to the table.

mtcarsDT[,cSumWtByCyl := cumsum(wt),by=cyl]

################################################################################################################################################################
# If the key column represents experiment tags, then we can remove duplicate experiments like so

unique(mtcarsDT)
    mpg cyl disp  hp drat    wt  qsec vs am gear carb        names cylTimesGear sumWtByCyl cylNames cSumWtByCyl
1: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1   Datsun 710           16     25.143    cyl=4       2.320
2: 19.7   6  145 175 3.62 2.770 15.50  0  1    5    6 Ferrari Dino           30     21.820    cyl=6       2.770
3: 15.2   8  304 150 3.15 3.435 17.30  0  0    3    2  AMC Javelin           24     55.989    cyl=8       3.435
################################################################################################################################################################
# Merge is much like with data.frames.  One nice feature is that key columns will be used for a merge automatically if they are set.

crbn <- data.table(carb=c(1,2,3), carbs=c("one", "two", "three"))
setkey(mtcarsDT, carb)
setkey(crbn, carb)
merge(mtcarsDT, crbn, all.x=TRUE)
    carb  mpg cyl  disp  hp drat    wt  qsec vs am gear               names cylTimesGear sumWtByCyl cylNames cSumWtByCyl carbs
 1:    1 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4          Datsun 710           16     25.143    cyl=4       2.320   one
 2:    1 32.4   4  78.7  66 4.08 2.200 19.47  1  1    4            Fiat 128           16     25.143    cyl=4       4.520   one
 3:    1 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4           Fiat X1-9           16     25.143    cyl=4       6.455   one
 4:    1 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4      Toyota Corolla           16     25.143    cyl=4      19.898   one
 5:    1 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3       Toyota Corona           12     25.143    cyl=4      22.363   one
 6:    1 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3      Hornet 4 Drive           18     21.820    cyl=6       5.985   one
 7:    1 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3             Valiant           18     21.820    cyl=6      21.820   one
 8:    2 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4         Honda Civic           16     25.143    cyl=4       8.070   two
 9:    2 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5        Lotus Europa           20     25.143    cyl=4       9.583   two
10:    2 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4            Merc 230           16     25.143    cyl=4      12.733   two
11:    2 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4           Merc 240D           16     25.143    cyl=4      15.923   two
12:    2 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5       Porsche 914-2           20     25.143    cyl=4      18.063   two
13:    2 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4          Volvo 142E           16     25.143    cyl=4      25.143   two
14:    2 15.2   8 304.0 150 3.15 3.435 17.30  0  0    3         AMC Javelin           24     55.989    cyl=8       3.435   two
15:    2 15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    Dodge Challenger           24     55.989    cyl=8      21.390   two
16:    2 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3   Hornet Sportabout           24     55.989    cyl=8      31.570   two
17:    2 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    Pontiac Firebird           24     55.989    cyl=8      55.989   two
18:    3 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3          Merc 450SE           24     55.989    cyl=8      44.634 three
19:    3 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3          Merc 450SL           24     55.989    cyl=8      48.364 three
20:    3 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3         Merc 450SLC           24     55.989    cyl=8      52.144 three
21:    4 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4           Mazda RX4           24     21.820    cyl=6       8.605    NA
22:    4 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4       Mazda RX4 Wag           24     21.820    cyl=6      11.480    NA
23:    4 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4            Merc 280           24     21.820    cyl=6      14.920    NA
24:    4 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4           Merc 280C           24     21.820    cyl=6      18.360    NA
25:    4 10.4   8 472.0 205 2.93 5.250 17.98  0  0    3  Cadillac Fleetwood           24     55.989    cyl=8       8.685    NA
26:    4 13.3   8 350.0 245 3.73 3.840 15.41  0  0    3          Camaro Z28           24     55.989    cyl=8      12.525    NA
27:    4 14.7   8 440.0 230 3.23 5.345 17.42  0  0    3   Chrysler Imperial           24     55.989    cyl=8      17.870    NA
28:    4 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3          Duster 360           24     55.989    cyl=8      24.960    NA
29:    4 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5      Ford Pantera L           40     55.989    cyl=8      28.130    NA
30:    4 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3 Lincoln Continental           24     55.989    cyl=8      36.994    NA
31:    6 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5        Ferrari Dino           30     21.820    cyl=6       2.770    NA
32:    8 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5       Maserati Bora           40     55.989    cyl=8      40.564    NA
    carb  mpg cyl  disp  hp drat    wt  qsec vs am gear               names cylTimesGear sumWtByCyl cylNames cSumWtByCyl carbs
################################################################################################################################################################
# Read a CSV into a data.table (much faster than read.csv & read.table).  With CSV, things usually just work.

df1 <- fread("dataTable_f1.csv")
df1
     name age weight
1:  babar  25    900
2: grumpy   2      3
################################################################################################################################################################
# Same as previous, but from a URL!!
df1u <- fread('https://www.mitchr.me/SS/exampleR/rcode/dataTable_f1.csv')
df1u
     name age weight
1:  babar  25    900
2: grumpy   2      3
################################################################################################################################################################
# Read output from a command (in this case compressing with gunzip and filtering with awk)

df2 <- fread("gunzip < dataTable_f2.csv.gz | awk -F, 'NR==1 || $3<100 { print $0 }'")
df2
     name age weight
1: george   7     15
################################################################################################################################################################
# Read a colon (:) separated file with extra whitespace and no column names

df3 <- fread('dataTable_f3.txt', sep=':', header=FALSE, strip.white=TRUE, col.names=c('name', 'age', 'weight'))
df3
      name age weight
1:   dumbo   1     50
2: thumper   2      1
3:   bamby   3     25

The R session information (including the OS info, R version and all packages used):

    options(width=80)
    sessionInfo()
R version 3.3.0 (2016-05-03)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 8 (jessie)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] graphics  grDevices datasets  methods   utils     grid      stats    
[8] base     

other attached packages:
 [1] RColorBrewer_1.1-2 reshape2_1.4.1     scales_0.4.0       ggplot2_2.1.0     
 [5] tidyr_0.5.1        dplyr_0.4.3        data.table_1.9.6   gridExtra_2.2.1   
 [9] jsonlite_1.0       knitr_1.13         lattice_0.20-33   

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.5      magrittr_1.5     munsell_0.4.3    colorspace_1.2-6
 [5] R6_2.1.2         stringr_1.0.0    highr_0.6        plyr_1.8.3      
 [9] tools_3.3.0      parallel_3.3.0   gtable_0.2.0     DBI_0.4-1       
[13] assertthat_0.1   tibble_1.1       formatR_1.4      curl_0.9.7      
[17] evaluate_0.9     stringi_1.0-1    chron_2.3-47    
    Sys.time()
[1] "2016-09-29 14:38:16 CDT"