灵活:重复 headers 而不更改列 class

Flextable: repeat headers without changing column class

对于很长的 table 秒,重复 headers 以获得更好的可见性可能很有用。例如,可以通过使用 names() 并使用 tibble::add_row 创建新列来预先在数据框上手动完成。

但是,问题是列必须相同 class,因此如果要添加列名,则必须将列转换为字符,这反过来会阻止 flextable 来自正确格式化数字。

函数add_body()最接近我的需求(https://davidgohel.github.io/flextable/reference/add_body.html)。它恰好允许您在 table 的 body 中添加新行。但是,它不允许您选择要将其准确添加到哪一行(就像 tibble::add_row 那样);只有两个选项:顶部或底部。此外,对于新行,必须单独指定每个 cell/column,因此我们不能依赖 names().

有什么方法可以实现我所描述的吗?谢谢。

编辑:即使使用 add_body() 我在添加具有不同 class 的新行时出错,在本例中日期为:

Error in as.POSIXlt.character(x, tz, ...) : 
  character string is not in a standard unambiguous format

确实如文档中所写:

It is important to insert data of the same type as the original data, otherwise it will be transformed (probably into strings if you add a character' where a double' is expected). This keeps the ability to format cell contents with the colformat_* functions, for example colformat_num().

编辑:我最好的解决方法是:

library(dplyr)
library(flextable)
ft <- flextable(head(iris))
ft %>%
    add_footer_row(values = names(iris),
                   colwidths = rep(1, length(names(iris))))

虽然额外的行在底部,但总比没有显示要好。

编辑 2022-01-06:

@jrcalabrese 的解决方案似乎适用于我提供的示例数据(iris 数据集)。这是一个演示,它甚至适用于不同的列 classes(例如日期、数字)和条件格式。

library(dplyr)
library(flextable)
iris2 <- cbind(iris, date = as.Date("2021-01-06"))
head(iris2) %>%
    mutate(across(everything(), as.character)) %>%
    add_row(Sepal.Length = "Sepal.Length", 
            Sepal.Width = "Sepal.Width",
            Petal.Length = "Petal.Length",
            Petal.Width = "Petal.Width",
            Species = "Species",
            date = "date", .before = 4) %>%
    flextable() -> ft2
ft2
ft2 %>%
    bg(i = ~ Petal.Length < 1.5,
       j = ft2$col_keys,
       bg = "grey")

不幸的是,正如@jrcalabrese 所提到的,无法使用 names() 自动执行此过程,因此我们必须手动定义每个行名称。

编辑 2022-01-07:

@romainfrancois has provided a tidiverse fix 以避免通过自定义 add_row2() 函数手动定义行名称。这是一个稍微改编的版本:

library(dplyr, warn.conflicts = FALSE)
library(flextable, warn.conflicts = FALSE)

# Define our new custom function
add_row2 <- function(.data, x, ...) {
  add_row(
    .data, 
    tibble(!!!setNames(x, names(.data))),
    ...
  )
}

# Add a date column
iris2 <- cbind(iris, date = as.Date("2021-01-06"))

# Add the header row
head(iris2) %>%
  mutate(across(everything(),
                as.character)) %>%
  add_row2(names(iris2),
           .before = 4) -> iris2
iris2
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species       date
#> 1          5.1         3.5          1.4         0.2  setosa 2021-01-06
#> 2          4.9           3          1.4         0.2  setosa 2021-01-06
#> 3          4.7         3.2          1.3         0.2  setosa 2021-01-06
#> 4 Sepal.Length Sepal.Width Petal.Length Petal.Width Species       date
#> 5          4.6         3.1          1.5         0.2  setosa 2021-01-06
#> 6            5         3.6          1.4         0.2  setosa 2021-01-06
#> 7          5.4         3.9          1.7         0.4  setosa 2021-01-06

# Make the flextable
ft2 <- flextable(iris2)
ft2 %>%
  bg(i = ~ Petal.Length < 1.5,
     j = ft2$col_keys,
     bg = "grey")

reprex package (v2.0.1)

创建于 2022-01-07

编辑 2022-01-15:

不幸的是,上述解决方案不适用于格式化小数等数字格式:

library(dplyr, warn.conflicts = FALSE)
library(flextable, warn.conflicts = FALSE)
    
# Multiply by a thousand to experiment with digit separators
iris2 <- iris*1000

# Make the flextable
ft2 <- flextable(iris2)
ft2 %>%
  colformat_double()    
# This works

# Define our new custom function
add_row2 <- function(.data, x, ...) {
  add_row(
    .data, 
    tibble(!!!setNames(x, names(.data))),
    ...
  )
}

# Add the header row
head(iris2) %>%
  mutate(across(everything(),
                as.character)) %>%
  add_row2(names(iris2),
           .before = 4) -> iris2

# Make the flextable again
ft2 <- flextable(iris2)
ft2 %>%
  colformat_double()
# This doesn't work (i.e., it doesn't format digits with the comma after thousands)

<sup>Created on 2022-01-15 by the [reprex package](https://reprex.tidyverse.org) (v2.0.1)</sup>

可能有更好的方法来做到这一点,但我认为它可以满足您的要求。它涉及将其转换为 huxtable,然后是 flextable,但不幸的是,必须为每第 n 行手动添加标题行。您还可以建议将此作为 flextable Github.

的潜在功能
library(tidyverse, warn.conflicts = FALSE)
library(flextable, warn.conflicts = FALSE)
library(huxtable, warn.conflicts = FALSE)

head(iris, 15) %>% 
  mutate(across(everything(), as.character)) %>%
  add_row(Sepal.Length = "Sepal.Length", 
          Sepal.Width = "Sepal.Width",
          Petal.Length = "Petal.Length",
          Petal.Width = "Petal.Width",
          Species = "Species", .before = 6) %>%
  add_row(Sepal.Length = "Sepal.Length", 
          Sepal.Width = "Sepal.Width",
          Petal.Length = "Petal.Length",
          Petal.Width = "Petal.Width",
          Species = "Species", .before = 11) %>%
  as_huxtable() %>%
  set_bold(row = 1, col = everywhere) %>% 
  set_bold(row = 7, col = everywhere) %>% 
  set_bold(row = 12, col = everywhere) %>%
  as_flextable() %>% 
  border_outer() %>% 
  border_inner_h()