-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Call for Tables - Lab Shell #29
Comments
Hi @Pierre-Wallet this is great, one point of clarification. Can you tell us what the various percentages are out of? For context in my work on Thanks! |
Hi @gmbecker, |
If the challenge is only for table format, that will be great if you could share reprex R code in preparing the data that's ready for output. If we could all based on the same data (e.g. https://cran.r-project.org/web/packages/safetyData/index.html). That will be helpful for us to understand the challenge in format this table using different R packages |
I took a shot at this with huxtable. library(huxtable)
set.seed(123)
dat <- data.frame(
Treatment = rep(c("A", "B"), 100),
`Baseline Grade` = sample(c(0:4, NA), 200, replace = TRUE),
PostGrade = sample(c(0:4, NA), 200, replace = TRUE),
check.names = FALSE
)
tbl <- xtabs( ~ PostGrade + `Baseline Grade` + Treatment, data = dat, addNA = TRUE)
ptbl <- proportions(tbl)
tbl <- addmargins(tbl, 1:3)
ptbl <- addmargins(ptbl, 1:3)
ptbl <- ptbl*100
joined <- tbl
joined[] <- paste0(tbl, " (", ptbl, ")")
output <- ftable(joined, col.vars = "PostGrade", row.vars = c("Treatment", "Baseline Grade"))
output <- as_hux(output)
# put 'Sum' column early, remove one empty column
output <- output[c(1:2, 10, 4:9)]
# tweak some headers
output[2, 1] <- ""
output[2, 2] <- ""
output[1, 3] <- ""
output |>
insert_row("", "Baseline", "", "Worst post-baseline value", fill = "") |>
set_colspan(1, 4, 6) |>
set_bottom_border(1, 4:9) |>
set_colspan(1, 2, 2) |>
set_align(1, everywhere, "center") |>
set_bold(1, everywhere) |>
insert_row("Treatment", "", fill = "n (%)", after = 2) |>
set_bottom_border(3, everywhere) |>
set_number_format(NA) |>
map_contents(by_regex("NA" = "Missing")) which gives Baseline Worst post-baseline value
─────────────────────────────────────────────────────────────────────────
0 1 2 3 4 Missing
Treatment n (%) n (%) n (%) n (%) n (%) n (%) n (%)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
A 0 17 (8.5) 3 (1.5) 3 (1.5) 2 (1) 3 (1.5) 4 (2) 2 (1)
1 16 (8) 3 (1.5) 3 (1.5) 5 (2.5) 2 (1) 3 (1.5) 0 (0)
2 14 (7) 1 (0.5) 3 (1.5) 2 (1) 2 (1) 5 (2.5) 1 (0.5)
3 16 (8) 3 (1.5) 0 (0) 3 (1.5) 5 (2.5) 3 (1.5) 2 (1)
4 19 (9.5) 5 (2.5) 3 (1.5) 7 (3.5) 2 (1) 0 (0) 2 (1)
Missing 18 (9) 3 (1.5) 2 (1) 1 (0.5) 6 (3) 4 (2) 2 (1)
Sum 100 (50) 18 (9) 14 (7) 20 (10) 20 (10) 19 (9.5) 9 (4.5)
B 0 16 (8) 4 (2) 2 (1) 1 (0.5) 0 (0) 3 (1.5) 6 (3)
1 15 (7.5) 5 (2.5) 3 (1.5) 2 (1) 0 (0) 3 (1.5) 2 (1)
2 18 (9) 3 (1.5) 5 (2.5) 3 (1.5) 1 (0.5) 5 (2.5) 1 (0.5)
3 12 (6) 6 (3) 3 (1.5) 0 (0) 1 (0.5) 1 (0.5) 1 (0.5)
4 15 (7.5) 1 (0.5) 8 (4) 1 (0.5) 2 (1) 1 (0.5) 2 (1)
Missing 24 (12) 3 (1.5) 6 (3) 1 (0.5) 5 (2.5) 6 (3) 3 (1.5)
Sum 100 (50) 22 (11) 27 (13.5) 8 (4) 9 (4.5) 19 (9.5) 15 (7.5)
Sum 0 33 (16.5) 7 (3.5) 5 (2.5) 3 (1.5) 3 (1.5) 7 (3.5) 8 (4)
1 31 (15.5) 8 (4) 6 (3) 7 (3.5) 2 (1) 6 (3) 2 (1)
2 32 (16) 4 (2) 8 (4) 5 (2.5) 3 (1.5) 10 (5) 2 (1)
3 28 (14) 9 (4.5) 3 (1.5) 3 (1.5) 6 (3) 4 (2) 3 (1.5)
4 34 (17) 6 (3) 11 (5.5) 8 (4) 4 (2) 1 (0.5) 4 (2)
Missing 42 (21) 6 (3) 8 (4) 2 (1) 11 (5.5) 10 (5) 5 (2.5)
Sum 200 (100) 40 (20) 41 (20.5) 28 (14) 29 (14.5) 38 (19) 24 (12)
Column names: V1, V2, V10, V4, V5, V6, V7, V8, V9 Indeed, not easy.
|
Here is an example using
output file: tmp.pdf
|
Hi, I've been keeping an eye on this repo since noticing my package, Below is some code outlining how the table above would be constructed with I'm still working on this package, so any suggestions would be more than welcome!
|
Hi all, @elong0527 @hughjonesd @gmbecker @ianmoran11 @davidgohel sorry for my late answer. In the solutions above, the percentages are not correctly calculated. The baseline percentage (1st column) is out of the number of patient in each treatment arm, and not out of the total number of patients. I did not focused on the sum of the 2 treatment arms, as it is optional in the display. I tried my best to get it with flextable+officer: Here is my code ( I added 2 parameters, the goal is to have one parameter per page)
|
Hi @Pierre-Wallet ! I just tried it using our table package NNtable.
Which gives:
|
Hi! I took time to do some work on flextable for this: package needs to be installed from github for these new functions. remotes::install_github("davidgohel/flextable") few format functionslibrary(data.table)
library(flextable)
multi_fun <- function(x) {
list(mean = mean(x),
sd = sd(x))
}
myformat <- function(z){
x <- sprintf("%.1f", z)
x[is.na(z)] <- ""
x
}
n_format <- function(z) {
x <- sprintf("%.0f", z)
x[is.na(z)] <- ""
x
}
data prepAggregations need to be prepared before using flextable. dat <- as.data.table(ggplot2::diamonds)
dat <- dat[cut %in% c("Fair", "Good", "Very Good")]
dat <- dat[clarity %in% c("I1", "SI1", "VS2")]
nstat <- dat[, list(n = .N),
by = c("cut")]
dat <- dat[, unlist(lapply(.SD, multi_fun),
recursive = FALSE),
.SDcols = c("z", "y"),
by = c("cut", "color", "clarity")]
dat
#> cut color clarity z.mean z.sd y.mean y.sd
#> 1: Very Good H SI1 3.817221 0.6664327 6.171371 1.0754836
#> 2: Fair E VS2 3.529286 0.5245551 5.449524 0.7673392
#> 3: Good J SI1 4.019773 0.7004727 6.430795 1.0808269
#> 4: Very Good J SI1 4.017088 0.6838668 6.510000 1.1041829
#> 5: Very Good E VS2 3.363797 0.5530067 5.466143 0.8988358
#> 6: Very Good J VS2 4.017500 0.6964981 6.513750 1.1436226
#> 7: Very Good D VS2 3.326278 0.5282660 5.382913 0.8421964
#> 8: Good H SI1 3.736894 0.6377995 5.987319 1.0611947
#> 9: Good D VS2 3.467596 0.5185969 5.538365 0.8182376
#> 10: Very Good F SI1 3.599481 0.5245868 5.827370 0.8484646
#> 11: Very Good I SI1 3.936257 0.6808834 6.356983 1.1074043
#> 12: Good I SI1 3.853758 0.7443514 6.147697 1.2262727
#> 13: Very Good G SI1 3.560823 0.5933639 5.761730 0.9487383
#> 14: Good E VS2 3.505000 0.5103667 5.677625 0.8019399
#> 15: Very Good E SI1 3.471629 0.5479509 5.619233 0.8996010
#> 16: Very Good D SI1 3.447955 0.5314568 5.594636 0.8764198
#> 17: Very Good F VS2 3.488906 0.5884464 5.661953 0.9465291
#> 18: Fair F VS2 3.593774 0.4990191 5.626226 0.6970742
#> 19: Very Good J I1 4.433750 0.5231754 7.186250 0.8307473
#> 20: Good I VS2 4.009364 0.6571069 6.468455 1.0810768
#> 21: Very Good G VS2 3.590731 0.6208030 5.814739 0.9953374
#> 22: Good G VS2 3.622865 0.5710884 5.819115 0.8951742
#> 23: Good F VS2 3.531576 0.5507295 5.663641 0.8695877
#> 24: Very Good H VS2 3.690452 0.6885978 5.972926 1.1102574
#> 25: Fair G SI1 3.852319 0.4371907 5.922754 0.6362248
#> 26: Fair E I1 4.008889 0.3876030 6.061111 0.4297221
#> 27: Good F SI1 3.568425 0.5056869 5.720879 0.8231311
#> 28: Good E SI1 3.486592 0.5638868 5.567803 0.9138209
#> 29: Fair I SI1 4.102000 0.4462905 6.402667 0.6600676
#> 30: Fair G I1 4.233962 0.7219374 6.434906 1.0849683
#> 31: Fair F I1 4.000857 0.7043536 6.040571 1.0820350
#> 32: Good G SI1 3.735024 0.5568696 5.952464 0.9221867
#> 33: Fair J VS2 3.960000 0.6183629 6.163478 1.0095976
#> 34: Very Good E I1 3.955000 0.5511784 6.436818 0.8837549
#> 35: Fair J SI1 4.135357 0.5361142 6.549643 0.8858454
#> 36: Good J VS2 4.007444 0.6286289 6.446000 1.0575940
#> 37: Very Good I VS2 3.917372 0.6876389 6.368686 1.1444792
#> 38: Fair F SI1 3.726506 0.5419464 5.871325 0.8438367
#> 39: Good I I1 4.305556 0.5688390 6.978889 1.0471920
#> 40: Good H VS2 3.690000 0.6633151 5.895725 1.0695806
#> 41: Good H I1 4.187857 0.5881807 6.745714 0.7694425
#> 42: Very Good H I1 4.600833 0.5658374 7.437500 0.8752259
#> 43: Fair H SI1 4.110533 0.5221341 6.355467 0.8221269
#> 44: Fair D VS2 3.704800 0.4685449 5.886800 0.7054828
#> 45: Good D SI1 3.450844 0.5407419 5.509409 0.8788101
#> 46: Fair E SI1 3.739077 0.5040763 5.903692 0.8057694
#> 47: Fair H I1 4.549231 0.7059102 6.858462 1.0348428
#> 48: Very Good I I1 4.628750 0.9238806 7.452500 1.4414749
#> 49: Fair I VS2 3.851875 0.4773342 6.154375 0.7318379
#> 50: Fair I I1 4.405294 0.5181508 6.653529 0.8454222
#> 51: Very Good G I1 4.050000 0.5254585 6.525000 0.8003749
#> 52: Fair J I1 4.990435 0.9209850 7.457391 1.3570832
#> 53: Good E I1 4.263913 0.5527760 6.898696 0.7825152
#> 54: Good G I1 3.869474 1.1021720 6.554737 0.9806255
#> 55: Fair H VS2 3.975122 0.5877462 6.161220 0.9045750
#> 56: Good F I1 3.861053 0.5899708 6.120000 0.9517820
#> 57: Fair D SI1 3.864828 0.4383792 5.957069 0.6556823
#> 58: Fair G VS2 3.955556 0.4868617 6.137333 0.7873442
#> 59: Very Good F I1 4.125385 0.5468793 6.714615 0.9804218
#> 60: Very Good D I1 3.886000 0.2823650 6.242000 0.4232257
#> 61: Good D I1 3.841250 0.6805552 6.293750 1.2610533
#> 62: Good J I1 4.310000 0.5615455 6.982500 0.7329563
#> 63: Fair D I1 4.905000 0.9796768 7.422500 1.4170009
#> cut color clarity z.mean z.sd y.mean y.sd
setDF(dat)
setDF(nstat) flextable creationgrey_txt <- fp_text_default(color = "gray")
cft_2 <- tabulator(
x = dat, rows = c("cut", "color"),
cols = "clarity",
hidden_data = nstat,
row_compose = list(
cut = as_paragraph(as_chunk(cut), " (N=", as_chunk(n, formatter = n_format), ")")
),
`z stats` = as_paragraph(
as_chunk(z.mean, formatter = myformat)),
`y stats` = as_paragraph(
as_chunk(y.mean, formatter = myformat),
as_chunk(" (\u00B1 ", props = grey_txt),
as_chunk(y.sd, formatter = myformat, props = grey_txt),
as_chunk(")", props = grey_txt)
)
)
ft_2 <- as_flextable(cft_2)
ft_2 <- autofit(x = ft_2, add_w = .05)
ft_2 <- color(ft_2, i = ~ cut %in% "Very Good", color = "blue")
ft_2 <- add_header_lines(x = ft_2, "blah blah blah blah blah blah blah blah blah blah")
ft_2 |
Hello all,
sharing here a template for lab output that is, according to me, gathering the most common difficulties in terms of formatting such as: one page per parameter, column spanners and cell merges.
I hope it helps!
Pierre.
The text was updated successfully, but these errors were encountered: