Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
|
@@ -1438,30 +1438,32 @@ create_tableau_location_plot <- function(pitcher_df, pitch_colors) {
|
|
| 1438 |
# =====================================================================
|
| 1439 |
create_tableau_movement_plot <- function(pitcher_df, pitch_colors) {
|
| 1440 |
filter_types <- get_valid_pitch_types(pitcher_df)
|
| 1441 |
-
|
| 1442 |
df <- pitcher_df %>%
|
| 1443 |
filter(TaggedPitchType %in% filter_types,
|
| 1444 |
!is.na(HorzBreak), !is.na(InducedVertBreak))
|
| 1445 |
-
|
| 1446 |
if (nrow(df) == 0) {
|
| 1447 |
-
return(ggplot() + theme_void() +
|
| 1448 |
labs(title = "Movement Profile") +
|
| 1449 |
theme(plot.title = element_text(size = 12, face = "bold", hjust = 0.5)))
|
| 1450 |
}
|
| 1451 |
-
|
| 1452 |
-
ggplot(df, aes(x = HorzBreak, y = InducedVertBreak
|
| 1453 |
geom_vline(xintercept = 0, color = "black", linewidth = 0.4) +
|
| 1454 |
geom_hline(yintercept = 0, color = "black", linewidth = 0.4) +
|
| 1455 |
-
|
| 1456 |
-
|
| 1457 |
-
|
|
|
|
|
|
|
| 1458 |
labs(title = "Movement Profile", x = "HB", y = "IVB") +
|
| 1459 |
theme_minimal() +
|
| 1460 |
theme(
|
| 1461 |
-
plot.title = element_text(size =
|
| 1462 |
legend.position = "none",
|
| 1463 |
-
axis.title = element_text(size =
|
| 1464 |
-
axis.text = element_text(size =
|
| 1465 |
plot.margin = margin(2, 2, 2, 2)
|
| 1466 |
)
|
| 1467 |
}
|
|
@@ -1471,116 +1473,163 @@ create_tableau_movement_plot <- function(pitcher_df, pitch_colors) {
|
|
| 1471 |
# =====================================================================
|
| 1472 |
create_tableau_release_plot <- function(pitcher_df, pitch_colors) {
|
| 1473 |
filter_types <- get_valid_pitch_types(pitcher_df)
|
| 1474 |
-
|
| 1475 |
df <- pitcher_df %>%
|
| 1476 |
filter(TaggedPitchType %in% filter_types,
|
| 1477 |
!is.na(RelSide), !is.na(RelHeight))
|
| 1478 |
-
|
| 1479 |
if (nrow(df) == 0) {
|
| 1480 |
-
return(ggplot() + theme_void() +
|
| 1481 |
labs(title = "Release Plot") +
|
| 1482 |
theme(plot.title = element_text(size = 10, face = "bold", hjust = 0.5)))
|
| 1483 |
}
|
| 1484 |
-
|
| 1485 |
-
# Mound arc
|
| 1486 |
mound_theta <- seq(0, pi, length.out = 100)
|
| 1487 |
mound_radius <- 3
|
| 1488 |
mound_df <- data.frame(
|
| 1489 |
x = mound_radius * cos(mound_theta),
|
| 1490 |
y = mound_radius * sin(mound_theta) * 0.35
|
| 1491 |
)
|
| 1492 |
-
|
| 1493 |
ggplot(df, aes(x = RelSide, y = RelHeight)) +
|
| 1494 |
-
geom_polygon(data = mound_df, aes(x = x, y = y),
|
| 1495 |
fill = "#C0392B", color = NA, inherit.aes = FALSE) +
|
| 1496 |
-
annotate("rect", xmin = -0.5, xmax = 0.5, ymin = 0.85, ymax = 1.05,
|
| 1497 |
fill = "white", color = "gray40", linewidth = 0.3) +
|
| 1498 |
geom_vline(xintercept = 0, color = "gray60", linetype = "dashed", linewidth = 0.3) +
|
| 1499 |
-
geom_point(aes(
|
| 1500 |
-
|
|
|
|
| 1501 |
coord_cartesian(xlim = c(-4, 4), ylim = c(0, 7)) +
|
| 1502 |
labs(title = "Release Plot", x = "Side", y = "Height") +
|
| 1503 |
theme_minimal() +
|
| 1504 |
theme(
|
| 1505 |
-
plot.title = element_text(size =
|
| 1506 |
legend.position = "none",
|
| 1507 |
-
axis.title = element_text(size =
|
| 1508 |
-
axis.text = element_text(size =
|
| 1509 |
plot.margin = margin(2, 2, 2, 2)
|
| 1510 |
)
|
| 1511 |
}
|
| 1512 |
|
| 1513 |
# =====================================================================
|
| 1514 |
-
# TABLE DRAWING -
|
| 1515 |
# =====================================================================
|
| 1516 |
-
|
| 1517 |
-
|
| 1518 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1519 |
# Title
|
| 1520 |
-
grid.text(
|
| 1521 |
-
|
| 1522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1523 |
# Column headers (pitch types)
|
| 1524 |
-
header_y <- y_start
|
| 1525 |
for (i in seq_along(pitch_types)) {
|
| 1526 |
pt <- pitch_types[i]
|
| 1527 |
-
col_color <- if (pt %in% names(pitch_colors)) pitch_colors[pt] else "#95A5A6"
|
| 1528 |
-
|
| 1529 |
-
grid.rect(
|
| 1530 |
-
|
| 1531 |
-
|
| 1532 |
-
|
| 1533 |
-
|
| 1534 |
-
|
| 1535 |
-
|
| 1536 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1537 |
pt_short <- gsub("Curveball", "CB", pt_short)
|
| 1538 |
pt_short <- gsub("Slider", "SL", pt_short)
|
| 1539 |
pt_short <- gsub("Sinker", "SI", pt_short)
|
| 1540 |
pt_short <- gsub("Cutter", "CT", pt_short)
|
| 1541 |
pt_short <- gsub("Splitter", "SP", pt_short)
|
| 1542 |
pt_short <- gsub("Sweeper", "SW", pt_short)
|
| 1543 |
-
|
| 1544 |
-
grid.text(
|
| 1545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1546 |
}
|
| 1547 |
-
|
| 1548 |
# Data rows
|
| 1549 |
-
|
| 1550 |
-
|
| 1551 |
-
|
| 1552 |
-
|
| 1553 |
-
|
| 1554 |
-
|
| 1555 |
-
|
| 1556 |
-
|
| 1557 |
-
|
| 1558 |
-
|
| 1559 |
-
|
| 1560 |
-
|
| 1561 |
-
|
| 1562 |
-
|
| 1563 |
-
|
|
|
|
|
|
|
| 1564 |
for (i in seq_along(pitch_types)) {
|
| 1565 |
pt <- pitch_types[i]
|
| 1566 |
idx <- which(data$TaggedPitchType == pt)
|
| 1567 |
-
|
| 1568 |
-
|
| 1569 |
-
|
| 1570 |
-
|
| 1571 |
}
|
| 1572 |
-
|
| 1573 |
-
grid.rect(
|
| 1574 |
-
|
| 1575 |
-
|
| 1576 |
-
|
| 1577 |
-
|
| 1578 |
-
|
| 1579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1580 |
}
|
| 1581 |
}
|
| 1582 |
}
|
| 1583 |
|
|
|
|
| 1584 |
# =====================================================================
|
| 1585 |
# MAIN PDF FUNCTION
|
| 1586 |
# =====================================================================
|
|
@@ -1708,56 +1757,52 @@ create_tableau_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 1708 |
# =====================================================================
|
| 1709 |
# TABLES - Farther right, more compact, bigger text
|
| 1710 |
# =====================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1711 |
|
| 1712 |
-
|
| 1713 |
-
|
| 1714 |
-
|
| 1715 |
-
|
| 1716 |
-
|
| 1717 |
-
|
| 1718 |
-
|
| 1719 |
-
|
| 1720 |
-
|
| 1721 |
-
|
| 1722 |
-
|
| 1723 |
-
|
| 1724 |
-
|
| 1725 |
-
|
| 1726 |
-
|
| 1727 |
-
|
| 1728 |
-
|
| 1729 |
-
|
| 1730 |
-
|
| 1731 |
-
|
| 1732 |
-
|
| 1733 |
-
|
| 1734 |
-
|
| 1735 |
-
|
| 1736 |
-
|
| 1737 |
-
|
| 1738 |
-
|
| 1739 |
-
|
| 1740 |
-
|
| 1741 |
-
|
| 1742 |
-
|
| 1743 |
-
|
| 1744 |
-
|
| 1745 |
-
pitch_colors = tableau_pitch_colors,
|
| 1746 |
-
x_start = table_x, y_start = 0.52,
|
| 1747 |
-
col_w = col_w, row_h = 0.013
|
| 1748 |
-
)
|
| 1749 |
-
|
| 1750 |
-
# Release Data table
|
| 1751 |
-
draw_tableau_table_v3(
|
| 1752 |
-
title = "Release Data",
|
| 1753 |
-
data = rel_data,
|
| 1754 |
-
metrics = c("Rel Ht", "vs FB", "Rel Side", "vs FB (S)", "Ext"),
|
| 1755 |
-
pitch_types = pitch_types,
|
| 1756 |
-
pitch_colors = tableau_pitch_colors,
|
| 1757 |
-
x_start = table_x, y_start = 0.36,
|
| 1758 |
-
col_w = col_w, row_h = 0.013
|
| 1759 |
-
)
|
| 1760 |
-
|
| 1761 |
invisible(output_file)
|
| 1762 |
}
|
| 1763 |
|
|
|
|
| 1438 |
# =====================================================================
|
| 1439 |
create_tableau_movement_plot <- function(pitcher_df, pitch_colors) {
|
| 1440 |
filter_types <- get_valid_pitch_types(pitcher_df)
|
| 1441 |
+
|
| 1442 |
df <- pitcher_df %>%
|
| 1443 |
filter(TaggedPitchType %in% filter_types,
|
| 1444 |
!is.na(HorzBreak), !is.na(InducedVertBreak))
|
| 1445 |
+
|
| 1446 |
if (nrow(df) == 0) {
|
| 1447 |
+
return(ggplot() + theme_void() +
|
| 1448 |
labs(title = "Movement Profile") +
|
| 1449 |
theme(plot.title = element_text(size = 12, face = "bold", hjust = 0.5)))
|
| 1450 |
}
|
| 1451 |
+
|
| 1452 |
+
ggplot(df, aes(x = HorzBreak, y = InducedVertBreak)) +
|
| 1453 |
geom_vline(xintercept = 0, color = "black", linewidth = 0.4) +
|
| 1454 |
geom_hline(yintercept = 0, color = "black", linewidth = 0.4) +
|
| 1455 |
+
# shape 21 uses fill for the inside color
|
| 1456 |
+
geom_point(aes(fill = TaggedPitchType), size = 4, alpha = 1,
|
| 1457 |
+
shape = 21, color = "black", stroke = 0.9) +
|
| 1458 |
+
scale_fill_manual(values = pitch_colors, drop = FALSE) +
|
| 1459 |
+
coord_cartesian(xlim = c(-30, 30), ylim = c(-30, 30)) +
|
| 1460 |
labs(title = "Movement Profile", x = "HB", y = "IVB") +
|
| 1461 |
theme_minimal() +
|
| 1462 |
theme(
|
| 1463 |
+
plot.title = element_text(size = 12, face = "bold", hjust = 0.5),
|
| 1464 |
legend.position = "none",
|
| 1465 |
+
axis.title = element_text(size = 10),
|
| 1466 |
+
axis.text = element_text(size = 9),
|
| 1467 |
plot.margin = margin(2, 2, 2, 2)
|
| 1468 |
)
|
| 1469 |
}
|
|
|
|
| 1473 |
# =====================================================================
|
| 1474 |
create_tableau_release_plot <- function(pitcher_df, pitch_colors) {
|
| 1475 |
filter_types <- get_valid_pitch_types(pitcher_df)
|
| 1476 |
+
|
| 1477 |
df <- pitcher_df %>%
|
| 1478 |
filter(TaggedPitchType %in% filter_types,
|
| 1479 |
!is.na(RelSide), !is.na(RelHeight))
|
| 1480 |
+
|
| 1481 |
if (nrow(df) == 0) {
|
| 1482 |
+
return(ggplot() + theme_void() +
|
| 1483 |
labs(title = "Release Plot") +
|
| 1484 |
theme(plot.title = element_text(size = 10, face = "bold", hjust = 0.5)))
|
| 1485 |
}
|
| 1486 |
+
|
|
|
|
| 1487 |
mound_theta <- seq(0, pi, length.out = 100)
|
| 1488 |
mound_radius <- 3
|
| 1489 |
mound_df <- data.frame(
|
| 1490 |
x = mound_radius * cos(mound_theta),
|
| 1491 |
y = mound_radius * sin(mound_theta) * 0.35
|
| 1492 |
)
|
| 1493 |
+
|
| 1494 |
ggplot(df, aes(x = RelSide, y = RelHeight)) +
|
| 1495 |
+
geom_polygon(data = mound_df, aes(x = x, y = y),
|
| 1496 |
fill = "#C0392B", color = NA, inherit.aes = FALSE) +
|
| 1497 |
+
annotate("rect", xmin = -0.5, xmax = 0.5, ymin = 0.85, ymax = 1.05,
|
| 1498 |
fill = "white", color = "gray40", linewidth = 0.3) +
|
| 1499 |
geom_vline(xintercept = 0, color = "gray60", linetype = "dashed", linewidth = 0.3) +
|
| 1500 |
+
geom_point(aes(fill = TaggedPitchType), size = 4, alpha = 1,
|
| 1501 |
+
shape = 21, color = "black", stroke = 0.9) +
|
| 1502 |
+
scale_fill_manual(values = pitch_colors, drop = FALSE) +
|
| 1503 |
coord_cartesian(xlim = c(-4, 4), ylim = c(0, 7)) +
|
| 1504 |
labs(title = "Release Plot", x = "Side", y = "Height") +
|
| 1505 |
theme_minimal() +
|
| 1506 |
theme(
|
| 1507 |
+
plot.title = element_text(size = 12, face = "bold", hjust = 0.5),
|
| 1508 |
legend.position = "none",
|
| 1509 |
+
axis.title = element_text(size = 10),
|
| 1510 |
+
axis.text = element_text(size = 9),
|
| 1511 |
plot.margin = margin(2, 2, 2, 2)
|
| 1512 |
)
|
| 1513 |
}
|
| 1514 |
|
| 1515 |
# =====================================================================
|
| 1516 |
+
# TABLE DRAWING (BIG + AUTO-SCALED TO FILL A RECTANGLE)
|
| 1517 |
# =====================================================================
|
| 1518 |
+
draw_tableau_table_fill <- function(
|
| 1519 |
+
title,
|
| 1520 |
+
data,
|
| 1521 |
+
rows, # a named vector: names = display label, values = column name in `data`
|
| 1522 |
+
pitch_types,
|
| 1523 |
+
pitch_colors,
|
| 1524 |
+
x, y, # TOP-LEFT anchor of the table block (npc)
|
| 1525 |
+
width, height # size of the table block (npc)
|
| 1526 |
+
) {
|
| 1527 |
+
if (is.null(pitch_types) || length(pitch_types) == 0) pitch_types <- "Undefined"
|
| 1528 |
+
|
| 1529 |
+
n_cols <- length(pitch_types)
|
| 1530 |
+
n_rows <- length(rows) + 1 # +1 header row
|
| 1531 |
+
|
| 1532 |
+
# Leave some space for the title
|
| 1533 |
+
title_h <- min(0.06, height * 0.20)
|
| 1534 |
+
table_top <- y - title_h
|
| 1535 |
+
table_h <- height - title_h
|
| 1536 |
+
|
| 1537 |
+
col_w <- width / n_cols
|
| 1538 |
+
row_h <- table_h / n_rows
|
| 1539 |
+
|
| 1540 |
+
# Scale font to row height (tuned for letter portrait)
|
| 1541 |
+
header_cex <- max(0.9, min(1.6, row_h * 28))
|
| 1542 |
+
body_cex <- max(0.85, min(1.5, row_h * 26))
|
| 1543 |
+
label_cex <- max(0.85, min(1.5, row_h * 26))
|
| 1544 |
+
title_cex <- max(1.1, min(1.9, row_h * 30))
|
| 1545 |
+
|
| 1546 |
# Title
|
| 1547 |
+
grid.text(
|
| 1548 |
+
title,
|
| 1549 |
+
x = x + width/2,
|
| 1550 |
+
y = y,
|
| 1551 |
+
gp = gpar(fontface = "bold", cex = title_cex, col = "#006F71")
|
| 1552 |
+
)
|
| 1553 |
+
|
| 1554 |
# Column headers (pitch types)
|
|
|
|
| 1555 |
for (i in seq_along(pitch_types)) {
|
| 1556 |
pt <- pitch_types[i]
|
| 1557 |
+
col_color <- if (pt %in% names(pitch_colors)) pitch_colors[[pt]] else "#95A5A6"
|
| 1558 |
+
|
| 1559 |
+
grid.rect(
|
| 1560 |
+
x = x + (i-1)*col_w,
|
| 1561 |
+
y = table_top,
|
| 1562 |
+
width = col_w * 0.98,
|
| 1563 |
+
height = row_h * 0.95,
|
| 1564 |
+
just = c("left", "top"),
|
| 1565 |
+
gp = gpar(fill = col_color, col = "gray30", lwd = 0.6)
|
| 1566 |
+
)
|
| 1567 |
+
|
| 1568 |
+
pt_short <- pt
|
| 1569 |
+
pt_short <- gsub("ChangeUp|Changeup", "CH", pt_short)
|
| 1570 |
+
pt_short <- gsub("Fastball|Four-Seam|FourSeamFastBall|Four-Seam Fastball|4-Seam Fastball|Four-Seam", "FB", pt_short)
|
| 1571 |
pt_short <- gsub("Curveball", "CB", pt_short)
|
| 1572 |
pt_short <- gsub("Slider", "SL", pt_short)
|
| 1573 |
pt_short <- gsub("Sinker", "SI", pt_short)
|
| 1574 |
pt_short <- gsub("Cutter", "CT", pt_short)
|
| 1575 |
pt_short <- gsub("Splitter", "SP", pt_short)
|
| 1576 |
pt_short <- gsub("Sweeper", "SW", pt_short)
|
| 1577 |
+
|
| 1578 |
+
grid.text(
|
| 1579 |
+
pt_short,
|
| 1580 |
+
x = x + (i-1)*col_w + col_w/2,
|
| 1581 |
+
y = table_top - row_h*0.48,
|
| 1582 |
+
gp = gpar(col = "white", cex = header_cex, fontface = "bold")
|
| 1583 |
+
)
|
| 1584 |
}
|
| 1585 |
+
|
| 1586 |
# Data rows
|
| 1587 |
+
row_names <- names(rows)
|
| 1588 |
+
col_names <- as.character(rows)
|
| 1589 |
+
|
| 1590 |
+
for (r in seq_along(col_names)) {
|
| 1591 |
+
disp <- row_names[r]
|
| 1592 |
+
coln <- col_names[r]
|
| 1593 |
+
y_row_top <- table_top - r*row_h
|
| 1594 |
+
|
| 1595 |
+
# Row label on the left (slightly outside the block)
|
| 1596 |
+
grid.text(
|
| 1597 |
+
disp,
|
| 1598 |
+
x = x - 0.010,
|
| 1599 |
+
y = y_row_top - row_h*0.55,
|
| 1600 |
+
just = "right",
|
| 1601 |
+
gp = gpar(cex = label_cex, fontface = "bold")
|
| 1602 |
+
)
|
| 1603 |
+
|
| 1604 |
for (i in seq_along(pitch_types)) {
|
| 1605 |
pt <- pitch_types[i]
|
| 1606 |
idx <- which(data$TaggedPitchType == pt)
|
| 1607 |
+
|
| 1608 |
+
val <- "-"
|
| 1609 |
+
if (length(idx) > 0 && coln %in% names(data)) {
|
| 1610 |
+
val <- as.character(data[[coln]][idx[1]])
|
| 1611 |
}
|
| 1612 |
+
|
| 1613 |
+
grid.rect(
|
| 1614 |
+
x = x + (i-1)*col_w,
|
| 1615 |
+
y = y_row_top,
|
| 1616 |
+
width = col_w * 0.98,
|
| 1617 |
+
height = row_h * 0.95,
|
| 1618 |
+
just = c("left", "top"),
|
| 1619 |
+
gp = gpar(fill = "white", col = "gray40", lwd = 0.6)
|
| 1620 |
+
)
|
| 1621 |
+
|
| 1622 |
+
grid.text(
|
| 1623 |
+
val,
|
| 1624 |
+
x = x + (i-1)*col_w + col_w/2,
|
| 1625 |
+
y = y_row_top - row_h*0.55,
|
| 1626 |
+
gp = gpar(cex = body_cex, fontface = "plain")
|
| 1627 |
+
)
|
| 1628 |
}
|
| 1629 |
}
|
| 1630 |
}
|
| 1631 |
|
| 1632 |
+
|
| 1633 |
# =====================================================================
|
| 1634 |
# MAIN PDF FUNCTION
|
| 1635 |
# =====================================================================
|
|
|
|
| 1757 |
# =====================================================================
|
| 1758 |
# TABLES - Farther right, more compact, bigger text
|
| 1759 |
# =====================================================================
|
| 1760 |
+
table_x <- 0.54
|
| 1761 |
+
table_w <- 0.43 # wide right block
|
| 1762 |
+
# Each table gets a tall rectangle
|
| 1763 |
+
draw_tableau_table_fill(
|
| 1764 |
+
title = "Location Data",
|
| 1765 |
+
data = loc_data,
|
| 1766 |
+
rows = c("Zone%"="Zone%", "Edge%"="Edge%", "Strike%"="Strike%", "Whiff%"="Whiff%"),
|
| 1767 |
+
pitch_types = pitch_types,
|
| 1768 |
+
pitch_colors = tableau_pitch_colors,
|
| 1769 |
+
x = table_x, y = 0.80, width = table_w, height = 0.18
|
| 1770 |
+
)
|
| 1771 |
+
|
| 1772 |
|
| 1773 |
+
draw_tableau_table_fill(
|
| 1774 |
+
title = "Pitch Usage",
|
| 1775 |
+
data = usage_data,
|
| 1776 |
+
rows = c("Usage vs. LHH"="Usage vs. LHH", "Usage vs. RHH"="Usage vs. RHH", "Pitch Count"="Pitch Count"),
|
| 1777 |
+
pitch_types = pitch_types,
|
| 1778 |
+
pitch_colors = tableau_pitch_colors,
|
| 1779 |
+
x = table_x, y = 0.60, width = table_w, height = 0.18
|
| 1780 |
+
)
|
| 1781 |
+
|
| 1782 |
+
draw_tableau_table_fill(
|
| 1783 |
+
title = "Velo & Movement",
|
| 1784 |
+
data = velo_data,
|
| 1785 |
+
rows = c("Avg. Velo"="Avg. Velo", "Max Velo"="Max. Velo", "Avg. Spin"="Avg. Spin",
|
| 1786 |
+
"Max Spin"="Max. Spin", "Avg. IVB"="Avg. IVB", "Avg. HB"="Avg. HB"),
|
| 1787 |
+
pitch_types = pitch_types,
|
| 1788 |
+
pitch_colors = tableau_pitch_colors,
|
| 1789 |
+
x = table_x, y = 0.40, width = table_w, height = 0.22
|
| 1790 |
+
)
|
| 1791 |
+
|
| 1792 |
+
# IMPORTANT: your old call was using fake column names, so it printed "-" everywhere.
|
| 1793 |
+
draw_tableau_table_fill(
|
| 1794 |
+
title = "Release Data",
|
| 1795 |
+
data = rel_data,
|
| 1796 |
+
rows = c("Rel Ht"="Avg. Rel Ht",
|
| 1797 |
+
"Rel Ht vs FB (in)"="Rel Ht vs. FB",
|
| 1798 |
+
"Rel Side"="Avg. Rel Side",
|
| 1799 |
+
"Rel Side vs FB (in)"="Rel Side vs. FB",
|
| 1800 |
+
"Ext"="Avg. Ext"),
|
| 1801 |
+
pitch_types = pitch_types,
|
| 1802 |
+
pitch_colors = tableau_pitch_colors,
|
| 1803 |
+
x = table_x, y = 0.14, width = table_w, height = 0.22
|
| 1804 |
+
)
|
| 1805 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1806 |
invisible(output_file)
|
| 1807 |
}
|
| 1808 |
|