Basic example

epitable contains some example data on trade balances:

library(epitable)
tradebalance
#> # A tibble: 12 x 7
#>    industry import_level import_share export_level export_share
#>    <chr>           <dbl>        <dbl>        <dbl>        <dbl>
#>  1 Total c…        403.        1             111.         1    
#>  2 Agricul…          2.3       0.006          17.3        0.156
#>  3 Mining           -0.1       0               8.5        0.077
#>  4 Oil and…          0.1       0               6.8        0.062
#>  5 Mineral…          0.1       0               1.7        0.015
#>  6 Manufac…        401.        0.994          80          0.72 
#>  7 Nondura…         44.1       0.109           3.5        0.032
#>  8 Food              3.2       0.008           2.5        0.023
#>  9 Beverag…          0.1       0               0.2        0.002
#> 10 Textile…         11.8       0.0290          0.4        0.004
#> 11 Apparel          20.7       0.051           0.1        0.001
#> 12 Leather…          8.3       0.02            0.3        0.003
#> # … with 2 more variables: balance_level <dbl>, balance_share <dbl>

Here is the simplest use of epitable:

epitable(tradebalance, rownamesvar = industry)
import_level import_share export_level export_share balance_level balance_share
Total change 403.2 1 111.1 1 -292.1 1
Agriculture, forestry, fishing, and hunting 2.3 0.006 17.3 0.156 15 -0.051
Mining -0.1 0 8.5 0.077 8.6 -0.03
Oil and gas 0.1 0 6.8 0.062 6.8 -0.023
Minerals and ores 0.1 0 1.7 0.015 1.6 -0.005
Manufacturing 400.8 0.994 80 0.72 -320.8 1.098
Nondurable goods 44.1 0.109 3.5 0.032 -40.6 0.139
Food 3.2 0.008 2.5 0.023 -0.6 0.002
Beverage and tobacco products 0.1 0 0.2 0.002 0.1 0
Textile mills and textile product mills 11.8 0.029 0.4 0.004 -11.4 0.039
Apparel 20.7 0.051 0.1 0.001 -20.7 0.071
Leather and allied products 8.3 0.02 0.3 0.003 -7.9 0.027

Note that you must specify the row names, either as a variable in the above, or as vector:

library(dplyr)
myrownames <- paste(c("My row"),seq(1:12))
tradebalance %>% select(-industry) %>% epitable(rownames = myrownames)
import_level import_share export_level export_share balance_level balance_share
My row 1 403.2 1 111.1 1 -292.1 1
My row 2 2.3 0.006 17.3 0.156 15 -0.051
My row 3 -0.1 0 8.5 0.077 8.6 -0.03
My row 4 0.1 0 6.8 0.062 6.8 -0.023
My row 5 0.1 0 1.7 0.015 1.6 -0.005
My row 6 400.8 0.994 80 0.72 -320.8 1.098
My row 7 44.1 0.109 3.5 0.032 -40.6 0.139
My row 8 3.2 0.008 2.5 0.023 -0.6 0.002
My row 9 0.1 0 0.2 0.002 0.1 0
My row 10 11.8 0.029 0.4 0.004 -11.4 0.039
My row 11 20.7 0.051 0.1 0.001 -20.7 0.071
My row 12 8.3 0.02 0.3 0.003 -7.9 0.027

Output options

As you’ve noticed, by default epitable’s output shows up in the RStudio viewer. You can also write the table snippet to a file:

epitable(tradebalance, rownamesvar=industry, file = "mytable.html")

That file will only contain the snippet of HTML that is the table. To write a fully self-contained web page, use the selfcontained option:

epitable(tradebalance, rownamesvar=industry, file = "mytable.html", selfcontained=TRUE)

To display the raw HTML of the table snippet in the console, try

print(epitable(tradebalance, rownamesvar=industry), useViewer=FALSE)
#> <table>
#> 
#> <thead>
#> <tr><th scope="col"></th> <th scope="col" style="text-align: right;" > import_level </th> <th scope="col" style="text-align: right;" > import_share </th> <th scope="col" style="text-align: right;" > export_level </th> <th scope="col" style="text-align: right;" > export_share </th> <th scope="col" style="text-align: right;" > balance_level </th> <th scope="col" style="text-align: right;" > balance_share </th></tr>
#> </thead>
#> 
#> <tbody>
#> <tr>
#> <th style="min-width:12em;" scope="row">Total change</th> 
#> <td style="font-variant-numeric: tabular-nums; text-align: right;" > 403.2 </td> 
...

Formatting entries

An easy way to format decimal points, percent signs, etc., is to use the scales package, which transforms columns of your dataset into character strings:

library(scales)

tradebalance_fmt <- tradebalance %>%  
  mutate_at(vars(ends_with("level")), ~number(.,accuracy=0.1, big.mark=",")) %>% 
  mutate_at(vars(ends_with("share")), ~percent(.,accuracy=0.1))

epitable(tradebalance_fmt, rownamesvar=industry)
import_level import_share export_level export_share balance_level balance_share
Total change 403.2 100.0% 111.1 100.0% -292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Row indendation

Row name indendation is handled by the rowlevels option:

epitable(tradebalance_fmt, rownamesvar=industry, rowlevels=rep(1:4,3))
import_level import_share export_level export_share balance_level balance_share
Total change 403.2 100.0% 111.1 100.0% -292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Here is an example of how you might do this a little less randomly:

thelevels <- tradebalance_fmt %>%
  mutate(level = 3) %>%
  mutate(level = ifelse(grepl("Total change|Agriculture, forestry|Mining|Manufacturing", .$industry), 1, level)) %>%
  mutate(level = ifelse(grepl("Oil and gas|Minerals and ores|Nondurable goods|Industrial supplies", .$industry), 2, level)) %>% 
  select(level) %>% 
  pull()

epitable(tradebalance_fmt, rownamesvar=industry, rowlevels=thelevels)
import_level import_share export_level export_share balance_level balance_share
Total change 403.2 100.0% 111.1 100.0% -292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Headers

Use more descriptive column names:

epitable(tradebalance_fmt, 
         rownamesvar=industry, 
         rowlevels=thelevels, 
         colnames=c("Import levels", 
                    "Import shares", 
                    "Export levels", 
                    "Export shares", 
                    "Trade balance levels", 
                    "Trade balance shares")
         )
Import levels Import shares Export levels Export shares Trade balance levels Trade balance shares
Total change 403.2 100.0% 111.1 100.0% -292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Add column groups:

epitable(tradebalance_fmt, 
         rownamesvar = industry, 
         rowlevels = thelevels,
         colnames = rep(c("Levels (\\$billions)", "Share of total change"), 3),
         colgroups = list(
           names = c("Imports", "Exports", "Trade balance"), 
           pattern = c(2, 2, 2))
         )
Imports Exports Trade balance
Levels ($billions) Share of total change Levels ($billions) Share of total change Levels ($billions) Share of total change
Total change 403.2 100.0% 111.1 100.0% -292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Combine tables

epitable_append() combines multiple tables into one. Note also the use of header=FALSE for table2 below:

# add dollar sign to Total change
data1 <- tradebalance_fmt %>% 
  filter(industry == "Total change") %>% 
  mutate_at(vars(ends_with("level")), ~paste0("$",.))
data2 <- tradebalance_fmt %>% filter(industry != "Total change")

table1 <- epitable(data1, 
                   rownamesvar = industry,
                   colnames = rep(c("Levels (\\$billions)", "Share of total change"),3),
                   colgroups = list(
                     names = c("Imports", "Exports", "Trade balance"), 
                     pattern = c(2, 2, 2))
                   )
table2 <- epitable(data2, 
                   rownamesvar=industry, 
                   rowlevels=thelevels[2:length(thelevels)], 
                   header=FALSE
                   )

epitable_append(table1, table2)
Imports Exports Trade balance
Levels ($billions) Share of total change Levels ($billions) Share of total change Levels ($billions) Share of total change
Total change $403.2 100.0% $111.1 100.0% $-292.1 100.0%
Agriculture, forestry, fishing, and hunting 2.3 0.6% 17.3 15.6% 15.0 -5.1%
Mining -0.1 0.0% 8.5 7.7% 8.6 -3.0%
Oil and gas 0.1 0.0% 6.8 6.2% 6.8 -2.3%
Minerals and ores 0.1 0.0% 1.7 1.5% 1.6 -0.5%
Manufacturing 400.8 99.4% 80.0 72.0% -320.8 109.8%
Nondurable goods 44.1 10.9% 3.5 3.2% -40.6 13.9%
Food 3.2 0.8% 2.5 2.3% -0.6 0.2%
Beverage and tobacco products 0.1 0.0% 0.2 0.2% 0.1 0.0%
Textile mills and textile product mills 11.8 2.9% 0.4 0.4% -11.4 3.9%
Apparel 20.7 5.1% 0.1 0.1% -20.7 7.1%
Leather and allied products 8.3 2.0% 0.3 0.3% -7.9 2.7%

Extra rows

epitable allows extra rows to be added to a table. The syntax is somewhat complicated (suggestions to improve it are very welcome!), but let’s see how it works with another example dataset educwages provided by the epitable package that contains mean wages by educational group:

epitable(educwages, rownamesvar = year)
educ1 educ2 educ3 educ4 educ5 stat
2000 12.59 17.85 20.32 31.27 39.47 level
2007 13.03 18.06 20.46 31.95 40.6 level
2017 13.66 18.25 20.01 33.17 42.39 level
2018 13.68 18.45 20.34 33.36 43.8 level
2000–2018 0.005 0.002 0 0.004 0.006 change
2000–2007 0.005 0.002 0.001 0.003 0.004 change
2007–2018 0.004 0.002 -0.001 0.004 0.007 change
2017–2018 0.001 0.011 0.016 0.006 0.033 change

The stat=level and stat=change amounts happen to be in dollars and percent, respectively. We can make that more clear in the resulting epitable by adding extra rows. First let’s format the data and create legible column names:

educwages_fmt <- educwages %>% 
  mutate_at(
    vars(starts_with("educ")), 
    ~ifelse(
      stat=="level", 
      paste0("$",number(., accuracy = 0.11)), 
      percent(.,accuracy=0.1)
    )
  ) %>% 
  select(year,starts_with("educ"))

educwages_colnames <- c(
  "Less than high school",
  "High school graduate",
  "Some college",
  "College graduate",
  "Advanced degree"
)

Now let’s add extra rows. To do this, include an argument called extrarowN that is a list of additional arguments. The extra row will be added before row N of the table.

The simplest example is to define the extrarowN list as the name of the row:

epitable(educwages_fmt, 
         rownamesvar = year,
         colnames = educwages_colnames,
         extrarow1 = list(name = "Levels"),
         extrarow5 = list(name = "Annualized percent changes")
)
Less than high school High school graduate Some college College graduate Advanced degree
Levels
2000 $12.54 $17.82 $20.35 $31.24 $39.49
2007 $12.98 $18.04 $20.46 $31.90 $40.59
2017 $13.64 $18.26 $20.02 $33.22 $42.35
2018 $13.64 $18.48 $20.35 $33.33 $43.78
Annualized percent changes
2000–2018 0.5% 0.2% 0.0% 0.4% 0.6%
2000–2007 0.5% 0.2% 0.1% 0.3% 0.4%
2007–2018 0.4% 0.2% -0.1% 0.4% 0.7%
2017–2018 0.1% 1.1% 1.6% 0.6% 3.3%

You could also use what EPI’s website calls “pseudoheader” rows:

epitable(educwages_fmt, 
         rownamesvar = year,
         colnames = educwages_colnames,
         extrarow1 = list(name = "Levels", pseudoheader=TRUE),
         extrarow5 = list(name = "Annualized percent change", pseudoheader=TRUE)
)
Less than high school High school graduate Some college College graduate Advanced degree
Levels
2000 $12.54 $17.82 $20.35 $31.24 $39.49
2007 $12.98 $18.04 $20.46 $31.90 $40.59
2017 $13.64 $18.26 $20.02 $33.22 $42.35
2018 $13.64 $18.48 $20.35 $33.33 $43.78
Annualized percent change
2000–2018 0.5% 0.2% 0.0% 0.4% 0.6%
2000–2007 0.5% 0.2% 0.1% 0.3% 0.4%
2007–2018 0.4% 0.2% -0.1% 0.4% 0.7%
2017–2018 0.1% 1.1% 1.6% 0.6% 3.3%

Finally, the easiest table to read might be to include additional lists of colgroups in the extrarowX lists. These require a vector of names and a vector of spanning patterns.

epitable(educwages_fmt, 
         rownamesvar = year,
         colnames = educwages_colnames,
         extrarow1 = list(colgroups = list(names = c("Levels"), 
                                           pattern = c(5))),
         extrarow5 = list(colgroups = list(names = c("Annualized percent changes"),
                                           pattern = c(5)))
)
Less than high school High school graduate Some college College graduate Advanced degree
Levels
2000 $12.54 $17.82 $20.35 $31.24 $39.49
2007 $12.98 $18.04 $20.46 $31.90 $40.59
2017 $13.64 $18.26 $20.02 $33.22 $42.35
2018 $13.64 $18.48 $20.35 $33.33 $43.78
Annualized percent changes
2000–2018 0.5% 0.2% 0.0% 0.4% 0.6%
2000–2007 0.5% 0.2% 0.1% 0.3% 0.4%
2007–2018 0.4% 0.2% -0.1% 0.4% 0.7%
2017–2018 0.1% 1.1% 1.6% 0.6% 3.3%