flextable layout

flextable layout can be easily managed. A set of functions will let you merge cells, add title rows, add footer notes, change the withs or heights.

library(flextable)
library(officer)

flextable col_keys

Parameter col_keys of function flextable define the variables to display and their order.

data <- iris[c(1:3, 51:53, 101:104),]
myft <- flextable(data, col_keys = c("Species", "Sepal.Length", "Petal.Length") )
myft

Species

Sepal.Length

Petal.Length

setosa

5.100

1.400

setosa

4.900

1.400

setosa

4.700

1.300

versicolor

7.000

4.700

versicolor

6.400

4.500

versicolor

6.900

4.900

virginica

6.300

6.000

virginica

5.800

5.100

virginica

7.100

5.900

virginica

6.300

5.600

If parameter col_keys has variables that are not existing in the dataset, they will be considered as blank columns and can be used as separators (in fact they can be use as you want, there is only no mapping of data associated).

myft <- flextable(
  data = data, 
  col_keys = c("Species", "col_1", "Sepal.Length", "Petal.Length") )
myft <- theme_vanilla(myft)
myft <- autofit(myft)
myft <- empty_blanks(myft)
myft

Species

Sepal.Length

Petal.Length

setosa

5.100

1.400

setosa

4.900

1.400

setosa

4.700

1.300

versicolor

7.000

4.700

versicolor

6.400

4.500

versicolor

6.900

4.900

virginica

6.300

6.000

virginica

5.800

5.100

virginica

7.100

5.900

virginica

6.300

5.600

col_keys default values are the names of the data.frame used to fill the flextable.

Change labels

Use set_header_labels() to replace labels of the bottom row of header. When the flextable is created, their values are the column names of the data.frame.

ft <- flextable( head( iris ) ) 
ft <- set_header_labels(ft, Sepal.Length = "Sepal length", 
    Sepal.Width = "Sepal width", Petal.Length = "Petal length",
    Petal.Width = "Petal width" )
ft

Sepal length

Sepal width

Petal length

Petal width

Species

5.100

3.500

1.400

0.200

setosa

4.900

3.000

1.400

0.200

setosa

4.700

3.200

1.300

0.200

setosa

4.600

3.100

1.500

0.200

setosa

5.000

3.600

1.400

0.200

setosa

5.400

3.900

1.700

0.400

setosa

New header rows can be added at the top or bottom of the header. This part in documented in part Manage headers and footers.

Under the hood, the names are in a single row data.frame associated with the header part of the flextable. You can add new rows later, they will be binded to that data.frame.

Cell merging

To illustrate functions, we will use a basic flextable example:


dat <- data.frame(
  letters1 = c("a", "b", "b", "c"), 
  letters2 = c("d", "e", "b", "b"), 
  number = 1:4, stringsAsFactors = FALSE )

myft <- flextable(dat)
myft <- theme_box(myft)
myft

letters1

letters2

number

a

d

1

b

e

2

b

b

3

c

b

4

vertical merging of similar values

merge_v() will merge adjacent duplicated cells for each column of the selection.

letters1

letters2

number

a

d

1

b

e

2

b

3

c

4

horizontal merging of similar values

merge_h() will merge adjacent duplicated cells for each row of the selection.

letters1

letters2

number

a

d

1

b

e

2

b

3

c

b

4

horizontal merging of columns

Function merge_h_range is close to the previous one but merge all colums between a range of columns.

letters1

letters2

number

a

1

b

2

b

b

3

c

b

4

general merging function

merge_at() will merge cells for a given continuous selection of rows and cells. The result is a single cell.

letters1

letters2

number

a

1

2

b

b

3

c

b

4

delete merging informations

If you want to get rid of all merging (i.e. for development purposes), use merge_none():

letters1

letters2

number

a

d

1

b

e

2

b

b

3

c

b

4

Borders and merging

When cells are merged, the rendered borders will be those of the first cell. If a column is made of three merged cells, the bottom border that will be seen will be the bottom border of the first cell in the column. From a user point of view, this is wrong, the bottom should be the one defined for cell 3. Function fix_border_issues is trying to fix that issue.

a

b

1

6

2

7

3

8

4

9

10

a

b

1

6

2

7

3

8

4

9

10

Manage headers and footers

Use one of the following functions to add an header row or a footer row:

We will mainly demonstrate headers but same can be apply with footers.

The operation below is changing labels that will be displayed instead of the original values (the names of dataset).

ft <- flextable( head( iris ) ) 
ft <- set_header_labels(ft, Sepal.Length = "Sepal", 
    Sepal.Width = "Sepal", Petal.Length = "Petal",
    Petal.Width = "Petal" )
# merge them 
ft <- merge_at(ft, i = 1, j = 1:2, part = "header")
ft <- merge_at(ft, i = 1, j = 3:4, part = "header")
ft

Sepal

Petal

Species

5.100

3.500

1.400

0.200

setosa

4.900

3.000

1.400

0.200

setosa

4.700

3.200

1.300

0.200

setosa

4.600

3.100

1.500

0.200

setosa

5.000

3.600

1.400

0.200

setosa

5.400

3.900

1.700

0.400

setosa

Now let’s add new row of labels.

Add a row of labels

Sepal

Petal

Species

length

width

length

width

5.100

3.500

1.400

0.200

setosa

4.900

3.000

1.400

0.200

setosa

4.700

3.200

1.300

0.200

setosa

4.600

3.100

1.500

0.200

setosa

5.000

3.600

1.400

0.200

setosa

5.400

3.900

1.700

0.400

setosa

Add lines of text

this is a second line

this is a first line

Sepal

Petal

Species

length

width

length

width

5.100

3.500

1.400

0.200

setosa

4.900

3.000

1.400

0.200

setosa

4.700

3.200

1.300

0.200

setosa

4.600

3.100

1.500

0.200

setosa

5.000

3.600

1.400

0.200

setosa

5.400

3.900

1.700

0.400

setosa

Define headers with a reference table

Use set_header_df() with a data.frame as parameter. Columns of the dataset will be transposed and joined using a key column.

  1. The reference table

Variable col_keys define key values to match with flextable column keys (defined by argument col_keys of flextable() function).

This key column will not be displayed. Other variables will added as rows. Note that variables names are not displayed.

  1. Use it as header rows

Then use set_header_df() with parameter key. key is the name of the column used to permform the join operation.

Order of columns matters, first column will be first row, second one will be the second row, etc.

double

factor

Sepal

Petal

Species

Length

Width

Length

Width

5.100

3.500

1.400

0.200

setosa

4.900

3.000

1.400

0.200

setosa

4.700

3.200

1.300

0.200

setosa

4.600

3.100

1.500

0.200

setosa

5.000

3.600

1.400

0.200

setosa

5.400

3.900

1.700

0.400

setosa

Cell widths and heights

The default sizes of flextable columns and rows are set by default values. This will drive to inadequate rows heights and columns widths in some cases. You can use function dim to get flextable dimensions.

ft_base <- flextable(head(mtcars))
ft_base <- theme_vader(ft_base, fontsize = 13)
ft_base

mpg

cyl

disp

hp

drat

wt

qsec

vs

am

gear

carb

21.000

6.000

160.000

110.000

3.900

2.620

16.460

0.000

1.000

4.000

4.000

21.000

6.000

160.000

110.000

3.900

2.875

17.020

0.000

1.000

4.000

4.000

22.800

4.000

108.000

93.000

3.850

2.320

18.610

1.000

1.000

4.000

1.000

21.400

6.000

258.000

110.000

3.080

3.215

19.440

1.000

0.000

3.000

1.000

18.700

8.000

360.000

175.000

3.150

3.440

17.020

0.000

0.000

3.000

2.000

18.100

6.000

225.000

105.000

2.760

3.460

20.220

1.000

0.000

3.000

1.000

dim(ft_base)
#> $widths
#>  mpg  cyl disp   hp drat   wt qsec   vs   am gear carb 
#> 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 
#> 
#> $heights
#> [1] 0.25 0.25 0.25 0.25 0.25 0.25 0.25

Pretty dimensions

Function dim_pretty() is computing optimized widths and heights.

Adjusts automatically cell widths and heights

Function autofit() optimises widths and heights of the flextable. This function is almost always to be called once when using flextable objects, it makes compact tables.

mpg

cyl

disp

hp

drat

wt

qsec

vs

am

gear

carb

21.000

6.000

160.000

110.000

3.900

2.620

16.460

0.000

1.000

4.000

4.000

21.000

6.000

160.000

110.000

3.900

2.875

17.020

0.000

1.000

4.000

4.000

22.800

4.000

108.000

93.000

3.850

2.320

18.610

1.000

1.000

4.000

1.000

21.400

6.000

258.000

110.000

3.080

3.215

19.440

1.000

0.000

3.000

1.000

18.700

8.000

360.000

175.000

3.150

3.440

17.020

0.000

0.000

3.000

2.000

18.100

6.000

225.000

105.000

2.760

3.460

20.220

1.000

0.000

3.000

1.000

Soft returns (a line break in a paragraph) support : function autofit and dim_pretty do not support soft returns and may return wrong results (will be considered as "").

Adjusts manually cell widths and heights

Function width() and height() let you control dimensions of a flextable. height_all() is an helper function to set the same height to each part of the table.

mpg

cyl

disp

hp

drat

wt

qsec

vs

am

gear

carb

21.000

6.000

160.000

110.000

3.900

2.620

16.460

0.000

1.000

4.000

4.000

21.000

6.000

160.000

110.000

3.900

2.875

17.020

0.000

1.000

4.000

4.000

22.800

4.000

108.000

93.000

3.850

2.320

18.610

1.000

1.000

4.000

1.000

21.400

6.000

258.000

110.000

3.080

3.215

19.440

1.000

0.000

3.000

1.000

18.700

8.000

360.000

175.000

3.150

3.440

17.020

0.000

0.000

3.000

2.000

18.100

6.000

225.000

105.000

2.760

3.460

20.220

1.000

0.000

3.000

1.000

Groups as row titles

Package flextable does not support data transformation. A grouped dataset is then needed (and tidy). This has to be done prior to the creation of the object.

Function as_grouped_data will modify data structure so that it will be easy to manage grouped data representation. Repeated consecutive values of group columns will be used to define the title of the groups and will be added as a row title.

Let’s have an example with aggragated data from dataset CO2:

library(data.table)
data_CO2 <- dcast(as.data.table(CO2), 
  Treatment + conc ~ Type, value.var = "uptake", fun.aggregate = mean)
head(data_CO2)
#>     Treatment conc   Quebec Mississippi
#> 1: nonchilled   95 15.26667    11.30000
#> 2: nonchilled  175 30.03333    20.20000
#> 3: nonchilled  250 37.40000    27.53333
#> 4: nonchilled  350 40.36667    29.90000
#> 5: nonchilled  500 39.60000    30.60000
#> 6: nonchilled  675 41.50000    30.53333

as_grouped_data will restructure the dataset:

data_CO2 <- as_grouped_data(x = data_CO2, groups = c("Treatment"))
head(data_CO2)
#>    Treatment conc   Quebec Mississippi
#> 1 nonchilled   NA       NA          NA
#> 3       <NA>   95 15.26667    11.30000
#> 4       <NA>  175 30.03333    20.20000
#> 5       <NA>  250 37.40000    27.53333
#> 6       <NA>  350 40.36667    29.90000
#> 7       <NA>  500 39.60000    30.60000

The result is suitable for method as_flextable. A call to this function and few formatting operations are producing the following result:

zz <- as_flextable( data_CO2 ) %>% 
  bold(j = 1, i = ~ !is.na(Treatment), bold = TRUE, part = "body" ) %>% 
  bold(part = "header", bold = TRUE ) %>% 
  width(width = 1.5)
zz

conc

Quebec

Mississippi

Treatment: nonchilled

95.000

15.267

11.300

175.000

30.033

20.200

250.000

37.400

27.533

350.000

40.367

29.900

500.000

39.600

30.600

675.000

41.500

30.533

1000.000

43.167

31.600

Treatment: chilled

95.000

12.867

9.600

175.000

24.133

14.767

250.000

34.467

16.100

350.000

35.800

16.600

500.000

36.667

16.633

675.000

37.500

18.267

1000.000

40.833

18.733

It’s now easier to customize the rendering. Let’s format column conc as an integer column:

zz <- zz %>% 
  compose(i = ~ !is.na(conc), j = "conc", 
          value = as_paragraph(
            as_chunk(conc, formater = function(x) sprintf("%.0f", x))
          )
  )
zz

conc

Quebec

Mississippi

Treatment: nonchilled

95

15.267

11.300

175

30.033

20.200

250

37.400

27.533

350

40.367

29.900

500

39.600

30.600

675

41.500

30.533

1000

43.167

31.600

Treatment: chilled

95

12.867

9.600

175

24.133

14.767

250

34.467

16.100

350

35.800

16.600

500

36.667

16.633

675

37.500

18.267

1000

40.833

18.733

Now let’s add nice bars before displaying the figures:

zz <- zz %>% 
  compose(i = ~ is.na(Treatment), j = "Quebec", 
          value = as_paragraph(
            minibar(Quebec), 
            " ", 
            as_chunk(Quebec, formater = function(x) sprintf("%.01f", x))
            )
          ) %>% 
  compose(i = ~ is.na(Treatment), j = "Mississippi", 
          value = as_paragraph( minibar(Mississippi), 
                                " ",
                                as_chunk(Mississippi, 
                                         formater = function(x) sprintf("%.01f", x) )
                                )
          ) %>% 
  align(j = 2:3, align = "left")
zz

conc

Quebec

Mississippi

Treatment: nonchilled

95

15.3

11.3

175

30.0

20.2

250

37.4

27.5

350

40.4

29.9

500

39.6

30.6

675

41.5

30.5

1000

43.2

31.6

Treatment: chilled

95

12.9

9.6

175

24.1

14.8

250

34.5

16.1

350

35.8

16.6

500

36.7

16.6

675

37.5

18.3

1000

40.8

18.7

And finally, add a footnote in the footer part:

add_footer_lines(zz, "dataset CO2 has been used for this flextable") 

conc

Quebec

Mississippi

Treatment: nonchilled

95

15.3

11.3

175

30.0

20.2

250

37.4

27.5

350

40.4

29.9

500

39.6

30.6

675

41.5

30.5

1000

43.2

31.6

Treatment: chilled

95

12.9

9.6

175

24.1

14.8

250

34.5

16.1

350

35.8

16.6

500

36.7

16.6

675

37.5

18.3

1000

40.8

18.7

dataset CO2 has been used for this flextable

xtable

xtable objects can be transformed as flextable objects with function xtable_to_flextable().

if( require("xtable") ){
  temp.ts <- ts(cumsum(1 + round(rnorm(100), 0)),
    start = c(1954, 7), frequency = 12)
  ft <- xtable_to_flextable(x = xtable(temp.ts, digits = 0),
    NA.string = "-")
  ft
}

Jan

Feb

Mar

Apr

May

Jun

Jul

Aug

Sep

Oct

Nov

Dec

1954

2.000

5.000

6.000

8.000

10.000

12.000

1955

14.000

16.000

19.000

21.000

24.000

25.000

26.000

28.000

28.000

27.000

28.000

31.000

1956

31.000

32.000

34.000

36.000

38.000

38.000

39.000

40.000

42.000

43.000

43.000

43.000

1957

43.000

45.000

44.000

45.000

45.000

47.000

49.000

50.000

51.000

52.000

54.000

54.000

1958

54.000

56.000

57.000

57.000

59.000

59.000

59.000

62.000

62.000

64.000

65.000

66.000

1959

68.000

69.000

71.000

72.000

74.000

76.000

76.000

79.000

80.000

82.000

85.000

86.000

1960

88.000

86.000

86.000

89.000

89.000

90.000

91.000

91.000

94.000

94.000

95.000

96.000

1961

96.000

95.000

97.000

99.000

100.000

101.000

102.000

103.000

104.000

104.000

106.000

106.000

1962

107.000

108.000

109.000

111.000

111.000

111.000

112.000

114.000

115.000

115.000