Spaces:
Sleeping
Sleeping
Update app.R
Browse files
app.R
CHANGED
|
@@ -17,7 +17,6 @@ library(tidyr)
|
|
| 17 |
library(stringr)
|
| 18 |
library(ggplot2)
|
| 19 |
library(scales)
|
| 20 |
-
library(jsonlite)
|
| 21 |
|
| 22 |
# ============================================================
|
| 23 |
# READ IN DATA
|
|
@@ -174,7 +173,7 @@ spring26_ncaa <- download_master_dataset("CoastalBaseball/2026MasterDataset", "n
|
|
| 174 |
college_join <- download_private_csv("CoastalBaseball/DefenseAppDataset", "college_join.csv")
|
| 175 |
|
| 176 |
# Load Catcher 2026 data
|
| 177 |
-
Catcher2026 <-
|
| 178 |
Catcher2026 <- Catcher2026 %>%
|
| 179 |
mutate(
|
| 180 |
Date = as.Date(Date),
|
|
@@ -190,14 +189,24 @@ Catcher2026 <- Catcher2026 %>%
|
|
| 190 |
)
|
| 191 |
|
| 192 |
# Classify framing: Strike Added / Strike Lost
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
Catcher2026 <- Catcher2026 %>%
|
| 195 |
mutate(
|
|
|
|
|
|
|
| 196 |
frame = case_when(
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
grepl("strike", PitchCall, ignore.case = TRUE) & !grepl("foul|swing", PitchCall, ignore.case = TRUE) & in_zone == 0 ~ "Strike Added",
|
| 200 |
TRUE ~ NA_character_
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
)
|
| 202 |
)
|
| 203 |
|
|
@@ -1030,10 +1039,10 @@ app_ui <- fluidPage(
|
|
| 1030 |
fluidRow(
|
| 1031 |
column(6,
|
| 1032 |
h4("Strikes Added", class = "section-header"),
|
| 1033 |
-
|
| 1034 |
column(6,
|
| 1035 |
h4("Strikes Lost", class = "section-header"),
|
| 1036 |
-
|
| 1037 |
),
|
| 1038 |
hr(),
|
| 1039 |
h4("Framing Log", class = "section-header"),
|
|
@@ -1086,7 +1095,7 @@ app_ui <- fluidPage(
|
|
| 1086 |
plotlyOutput("block_ground_plot", height = "500px")),
|
| 1087 |
column(6,
|
| 1088 |
h4("Blocking (Zone View)", class = "section-header"),
|
| 1089 |
-
|
| 1090 |
),
|
| 1091 |
hr(),
|
| 1092 |
h4("Blocking Log", class = "section-header"),
|
|
@@ -1709,7 +1718,12 @@ server <- function(input, output, session) {
|
|
| 1709 |
})
|
| 1710 |
|
| 1711 |
|
| 1712 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1713 |
plotly_zone_shapes <- function() {
|
| 1714 |
list(
|
| 1715 |
list(type="rect", x0=-.83083, x1=.83083, y0=1.5, y1=3.3775,
|
|
@@ -1722,87 +1736,111 @@ server <- function(input, output, session) {
|
|
| 1722 |
)
|
| 1723 |
}
|
| 1724 |
|
| 1725 |
-
|
| 1726 |
-
|
| 1727 |
-
|
| 1728 |
-
|
| 1729 |
-
|
| 1730 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1731 |
|
| 1732 |
-
|
| 1733 |
-
|
| 1734 |
-
|
| 1735 |
-
|
| 1736 |
-
|
| 1737 |
-
|
| 1738 |
-
|
| 1739 |
-
|
| 1740 |
-
|
| 1741 |
-
|
| 1742 |
-
|
| 1743 |
-
|
| 1744 |
-
|
| 1745 |
-
|
| 1746 |
-
|
| 1747 |
-
|
| 1748 |
-
|
| 1749 |
-
|
| 1750 |
-
|
| 1751 |
-
line=list(color="black",width=1), fillcolor="rgba(200,200,200,0.3)"),
|
| 1752 |
-
list(type="path",
|
| 1753 |
-
path="M -0.708 0.15 L 0.708 0.15 L 0.708 0.3 L 0 0.5 L -0.708 0.3 Z",
|
| 1754 |
-
line=list(color="black",width=1.5), fillcolor="rgba(240,240,240,0.5)"))
|
| 1755 |
-
)
|
| 1756 |
-
return(p)
|
| 1757 |
-
}
|
| 1758 |
|
| 1759 |
-
|
| 1760 |
-
|
| 1761 |
-
|
| 1762 |
-
|
| 1763 |
-
|
| 1764 |
-
|
| 1765 |
-
|
| 1766 |
-
|
| 1767 |
-
|
| 1768 |
-
|
| 1769 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1770 |
}
|
| 1771 |
-
|
| 1772 |
-
# Default: Points mode — pure plotly
|
| 1773 |
-
data <- data %>% mutate(row_num = row_number())
|
| 1774 |
-
pt_colors <- sapply(data$TaggedPitchType, function(pt) {
|
| 1775 |
-
if (!is.na(pt) && pt %in% names(pitch_colors)) pitch_colors[pt] else "grey60"
|
| 1776 |
-
})
|
| 1777 |
-
p <- plot_ly(data, x=~PlateLocSide, y=~PlateLocHeight, type="scatter", mode="markers+text",
|
| 1778 |
-
marker=list(size=14, color=pt_colors, line=list(color="black", width=1.2)),
|
| 1779 |
-
text=~as.character(row_num), textfont=list(size=8, color="white"),
|
| 1780 |
-
textposition="middle center",
|
| 1781 |
-
hovertext=~paste0("#",row_num,"<br>Pitch: ",TaggedPitchType,
|
| 1782 |
-
"<br>Pitcher: ",Pitcher,"<br>Batter: ",Batter,
|
| 1783 |
-
"<br>Velo: ",round(RelSpeed,1)," mph",
|
| 1784 |
-
"<br>Count: ",Balls,"-",Strikes,
|
| 1785 |
-
"<br>RV: ",round(mean_DRE,3),"<br>Date: ",Date),
|
| 1786 |
-
hoverinfo="text") %>%
|
| 1787 |
-
layout(title=list(text=title_text, font=list(size=14)),
|
| 1788 |
-
xaxis=list(title="", range=c(-1.8,1.8), zeroline=FALSE, showticklabels=FALSE, scaleanchor="y"),
|
| 1789 |
-
yaxis=list(title="", range=c(0,4.5), zeroline=FALSE, showticklabels=FALSE),
|
| 1790 |
-
shapes=plotly_zone_shapes(),
|
| 1791 |
-
showlegend=FALSE)
|
| 1792 |
-
p
|
| 1793 |
-
}
|
| 1794 |
-
|
| 1795 |
-
output$recv_strikes_added <- renderPlotly({
|
| 1796 |
-
d <- recv_data() %>% filter(frame == "Strike Added")
|
| 1797 |
-
make_recv_plot(d, "Strikes Added", input$recv_view_mode)
|
| 1798 |
})
|
| 1799 |
-
|
| 1800 |
-
output$recv_strikes_lost <-
|
| 1801 |
-
d <- recv_data() %>% filter(frame == "Strike Lost")
|
| 1802 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1803 |
})
|
| 1804 |
-
|
| 1805 |
-
|
| 1806 |
d <- recv_data() %>%
|
| 1807 |
filter(!is.na(frame)) %>%
|
| 1808 |
dplyr::select(Date, Inning, Pitcher, Batter, TaggedPitchType,
|
|
@@ -2149,48 +2187,43 @@ server <- function(input, output, session) {
|
|
| 2149 |
ggplotly(p, tooltip="text")
|
| 2150 |
})
|
| 2151 |
|
| 2152 |
-
output$block_zone_plot <-
|
| 2153 |
bd <- block_data() %>%
|
| 2154 |
filter(!is.na(PlateLocSide) & !is.na(PlateLocHeight)) %>%
|
| 2155 |
mutate(row_num = row_number())
|
| 2156 |
-
|
| 2157 |
view_mode <- input$block_view_mode
|
| 2158 |
-
|
| 2159 |
-
|
| 2160 |
-
|
| 2161 |
-
}
|
| 2162 |
-
|
| 2163 |
if (view_mode == "Heatmap" && nrow(bd) >= 3) {
|
| 2164 |
-
|
| 2165 |
-
colorscale=list(c(0,"white"),c(0.25,"#0551bc"),c(0.5,"#03ff00"),c(0.75,"#fbff00"),c(1,"#dc1100")),
|
| 2166 |
-
showscale=FALSE) %>%
|
| 2167 |
-
layout(title=list(text="Blocking (Zone) - Heatmap", font=list(size=14)),
|
| 2168 |
-
xaxis=list(title="", range=c(-2.5,2.5), scaleanchor="y", showticklabels=FALSE),
|
| 2169 |
-
yaxis=list(title="", range=c(-1,4.5), showticklabels=FALSE),
|
| 2170 |
-
shapes=plotly_zone_shapes())
|
| 2171 |
-
return(p)
|
| 2172 |
}
|
| 2173 |
-
|
| 2174 |
-
|
| 2175 |
-
|
| 2176 |
-
|
| 2177 |
-
|
| 2178 |
-
|
| 2179 |
-
|
| 2180 |
-
|
| 2181 |
-
|
| 2182 |
-
|
| 2183 |
-
|
| 2184 |
-
|
| 2185 |
-
|
| 2186 |
-
|
| 2187 |
-
|
| 2188 |
-
|
| 2189 |
-
|
| 2190 |
-
|
| 2191 |
-
|
| 2192 |
-
|
| 2193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2194 |
})
|
| 2195 |
|
| 2196 |
output$block_log_table <- renderDT({
|
|
|
|
| 17 |
library(stringr)
|
| 18 |
library(ggplot2)
|
| 19 |
library(scales)
|
|
|
|
| 20 |
|
| 21 |
# ============================================================
|
| 22 |
# READ IN DATA
|
|
|
|
| 173 |
college_join <- download_private_csv("CoastalBaseball/DefenseAppDataset", "college_join.csv")
|
| 174 |
|
| 175 |
# Load Catcher 2026 data
|
| 176 |
+
Catcher2026 <- download_private_parquet("CoastalBaseball/DefenseAppDataset", "Catcher2026.parquet")
|
| 177 |
Catcher2026 <- Catcher2026 %>%
|
| 178 |
mutate(
|
| 179 |
Date = as.Date(Date),
|
|
|
|
| 189 |
)
|
| 190 |
|
| 191 |
# Classify framing: Strike Added / Strike Lost
|
| 192 |
+
STRIKE_CALLS <- c("StrikeCalled","Called Strike","strike_called","StrikeSwinging",
|
| 193 |
+
"FoulBall","InPlay","Foul","FoulBallNotFieldable",
|
| 194 |
+
"FoulBallFieldable","FoulTip","Foul Tip")
|
| 195 |
+
BALL_CALLS <- c("BallCalled","Ball","ball_called","BallinDirt","BallIntentional",
|
| 196 |
+
"HitByPitch","ball","AutomaticBall")
|
| 197 |
Catcher2026 <- Catcher2026 %>%
|
| 198 |
mutate(
|
| 199 |
+
StolenStrike = as.integer(in_zone == 0 & PitchCall %in% STRIKE_CALLS),
|
| 200 |
+
StrikeLost = as.integer(in_zone == 1 & PitchCall %in% BALL_CALLS),
|
| 201 |
frame = case_when(
|
| 202 |
+
in_zone == 1 & PitchCall %in% BALL_CALLS ~ "Strike Lost",
|
| 203 |
+
in_zone == 0 & PitchCall %in% STRIKE_CALLS ~ "Strike Added",
|
|
|
|
| 204 |
TRUE ~ NA_character_
|
| 205 |
+
),
|
| 206 |
+
frame_numeric = case_when(
|
| 207 |
+
in_zone == 1 & PitchCall %in% BALL_CALLS ~ -1,
|
| 208 |
+
in_zone == 0 & PitchCall %in% STRIKE_CALLS ~ 1,
|
| 209 |
+
TRUE ~ NA_real_
|
| 210 |
)
|
| 211 |
)
|
| 212 |
|
|
|
|
| 1039 |
fluidRow(
|
| 1040 |
column(6,
|
| 1041 |
h4("Strikes Added", class = "section-header"),
|
| 1042 |
+
plotOutput("recv_strikes_added", height = "480px")),
|
| 1043 |
column(6,
|
| 1044 |
h4("Strikes Lost", class = "section-header"),
|
| 1045 |
+
plotOutput("recv_strikes_lost", height = "480px"))
|
| 1046 |
),
|
| 1047 |
hr(),
|
| 1048 |
h4("Framing Log", class = "section-header"),
|
|
|
|
| 1095 |
plotlyOutput("block_ground_plot", height = "500px")),
|
| 1096 |
column(6,
|
| 1097 |
h4("Blocking (Zone View)", class = "section-header"),
|
| 1098 |
+
plotOutput("block_zone_plot", height = "500px"))
|
| 1099 |
),
|
| 1100 |
hr(),
|
| 1101 |
h4("Blocking Log", class = "section-header"),
|
|
|
|
| 1718 |
})
|
| 1719 |
|
| 1720 |
|
| 1721 |
+
# Zone plot geometry
|
| 1722 |
+
szr <- data.frame(xmin=-.83083, xmax=.83083, ymin=1.5, ymax=3.3775)
|
| 1723 |
+
bzr <- data.frame(xmin=-.9975, xmax=.9975, ymin=1.3775, ymax=3.5)
|
| 1724 |
+
hp_recv <- data.frame(x=c(-.60,.60,.60,0,-.60), y=c(.15,.15,.27,.42,.27))
|
| 1725 |
+
|
| 1726 |
+
# Plotly zone shapes for catch position overhead
|
| 1727 |
plotly_zone_shapes <- function() {
|
| 1728 |
list(
|
| 1729 |
list(type="rect", x0=-.83083, x1=.83083, y0=1.5, y1=3.3775,
|
|
|
|
| 1736 |
)
|
| 1737 |
}
|
| 1738 |
|
| 1739 |
+
# Core zone plot function (ggplot, NO plotly)
|
| 1740 |
+
make_zone_plot <- function(data, title) {
|
| 1741 |
+
if (!nrow(data)) return(
|
| 1742 |
+
ggplot() + theme_void() + ggtitle(title) +
|
| 1743 |
+
theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
|
| 1744 |
+
apt <- unique(data$TaggedPitchType[!is.na(data$TaggedPitchType)])
|
| 1745 |
+
ggplot(data, aes(PlateLocSide, PlateLocHeight)) +
|
| 1746 |
+
geom_rect(data=bzr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
|
| 1747 |
+
fill=NA, color="gray50", linewidth=.5, linetype="dotted", inherit.aes=FALSE) +
|
| 1748 |
+
geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
|
| 1749 |
+
fill=NA, color="black", linewidth=.7, inherit.aes=FALSE) +
|
| 1750 |
+
geom_polygon(data=hp_recv, aes(x=x,y=y),
|
| 1751 |
+
fill=NA, color="gray40", linewidth=.4, inherit.aes=FALSE) +
|
| 1752 |
+
geom_point(aes(fill=TaggedPitchType), shape=21, size=5,
|
| 1753 |
+
color="black", stroke=.6, alpha=.95, na.rm=TRUE) +
|
| 1754 |
+
geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white", na.rm=TRUE) +
|
| 1755 |
+
scale_fill_manual(values=pitch_colors, na.value="grey60",
|
| 1756 |
+
name="Pitch Type", drop=FALSE, limits=apt) +
|
| 1757 |
+
coord_equal() +
|
| 1758 |
+
scale_x_continuous(limits=c(-1.8,1.8)) +
|
| 1759 |
+
scale_y_continuous(limits=c(0,4.5)) +
|
| 1760 |
+
labs(title=title) + theme_classic() +
|
| 1761 |
+
theme(axis.title=element_blank(), axis.text=element_blank(),
|
| 1762 |
+
axis.ticks=element_blank(), axis.line=element_blank(),
|
| 1763 |
+
panel.grid=element_blank(),
|
| 1764 |
+
plot.title=element_text(hjust=.5, size=10, face="bold"),
|
| 1765 |
+
plot.margin=margin(2,2,2,2), legend.position="none")
|
| 1766 |
+
}
|
| 1767 |
|
| 1768 |
+
# Heatmap zone plot function (ggplot, NO plotly)
|
| 1769 |
+
make_zone_heatmap <- function(data, title) {
|
| 1770 |
+
if (nrow(data) < 3) return(
|
| 1771 |
+
ggplot() + theme_void() + ggtitle(paste(title, "- Not enough data")) +
|
| 1772 |
+
theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
|
| 1773 |
+
ggplot(data, aes(x=PlateLocSide, y=PlateLocHeight)) +
|
| 1774 |
+
stat_density_2d(aes(fill=after_stat(density)), geom="raster", contour=FALSE) +
|
| 1775 |
+
scale_fill_gradientn(colours=c("white","#0551bc","#02fbff","#03ff00",
|
| 1776 |
+
"#fbff00","#ffa503","#ff1f02","#dc1100"),
|
| 1777 |
+
name="Density") +
|
| 1778 |
+
geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
|
| 1779 |
+
fill=NA, color="black", linewidth=1, inherit.aes=FALSE) +
|
| 1780 |
+
geom_polygon(data=hp_recv, aes(x=x,y=y),
|
| 1781 |
+
fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
|
| 1782 |
+
coord_fixed(ratio=1) + xlim(-2,2) + ylim(0,4.5) +
|
| 1783 |
+
ggtitle(title) + theme_void() +
|
| 1784 |
+
theme(legend.position="right", plot.margin=margin(3,3,3,3),
|
| 1785 |
+
plot.title=element_text(hjust=0.5, size=12, face="bold"))
|
| 1786 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1787 |
|
| 1788 |
+
output$recv_strikes_added <- renderPlot({
|
| 1789 |
+
d <- recv_data() %>% filter(frame == "Strike Added") %>% mutate(row_num = row_number())
|
| 1790 |
+
vm <- input$recv_view_mode
|
| 1791 |
+
if (vm == "Heatmap") {
|
| 1792 |
+
make_zone_heatmap(d, "Strikes Added")
|
| 1793 |
+
} else if (vm == "Catch Position (Overhead)") {
|
| 1794 |
+
# Render as ggplot with batters box
|
| 1795 |
+
d2 <- d %>% filter(!is.na(CatchPositionX) & !is.na(CatchPositionZ)) %>%
|
| 1796 |
+
mutate(cpx = as.numeric(CatchPositionX), cpz = as.numeric(CatchPositionZ))
|
| 1797 |
+
if (nrow(d2) == 0) return(ggplot() + theme_void() + ggtitle("No catch position data"))
|
| 1798 |
+
bi <- 1; bo <- 4; bymin <- -2.5; bymax <- 3.7
|
| 1799 |
+
plate <- data.frame(x=c(-.708,.708,.708,0,-.708), y=c(1.417,1.417,.708,0,.708))
|
| 1800 |
+
apt <- unique(d2$TaggedPitchType[!is.na(d2$TaggedPitchType)])
|
| 1801 |
+
ggplot(d2, aes(x=cpx, y=cpz)) +
|
| 1802 |
+
annotate("rect", xmin=bi, xmax=bo, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
|
| 1803 |
+
annotate("rect", xmin=-bo, xmax=-bi, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
|
| 1804 |
+
geom_polygon(data=plate, aes(x=x,y=y), fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
|
| 1805 |
+
geom_point(aes(fill=TaggedPitchType), shape=21, size=4, color="black", stroke=.6, alpha=.95) +
|
| 1806 |
+
geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white") +
|
| 1807 |
+
scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", drop=FALSE, limits=apt) +
|
| 1808 |
+
coord_fixed() + labs(title="Strikes Added - Catch Position") +
|
| 1809 |
+
theme_void() + theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
|
| 1810 |
+
legend.position="bottom")
|
| 1811 |
+
} else {
|
| 1812 |
+
make_zone_plot(d, "Strikes Added")
|
| 1813 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1814 |
})
|
| 1815 |
+
|
| 1816 |
+
output$recv_strikes_lost <- renderPlot({
|
| 1817 |
+
d <- recv_data() %>% filter(frame == "Strike Lost") %>% mutate(row_num = row_number())
|
| 1818 |
+
vm <- input$recv_view_mode
|
| 1819 |
+
if (vm == "Heatmap") {
|
| 1820 |
+
make_zone_heatmap(d, "Strikes Lost")
|
| 1821 |
+
} else if (vm == "Catch Position (Overhead)") {
|
| 1822 |
+
d2 <- d %>% filter(!is.na(CatchPositionX) & !is.na(CatchPositionZ)) %>%
|
| 1823 |
+
mutate(cpx = as.numeric(CatchPositionX), cpz = as.numeric(CatchPositionZ))
|
| 1824 |
+
if (nrow(d2) == 0) return(ggplot() + theme_void() + ggtitle("No catch position data"))
|
| 1825 |
+
bi <- 1; bo <- 4; bymin <- -2.5; bymax <- 3.7
|
| 1826 |
+
plate <- data.frame(x=c(-.708,.708,.708,0,-.708), y=c(1.417,1.417,.708,0,.708))
|
| 1827 |
+
apt <- unique(d2$TaggedPitchType[!is.na(d2$TaggedPitchType)])
|
| 1828 |
+
ggplot(d2, aes(x=cpx, y=cpz)) +
|
| 1829 |
+
annotate("rect", xmin=bi, xmax=bo, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
|
| 1830 |
+
annotate("rect", xmin=-bo, xmax=-bi, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
|
| 1831 |
+
geom_polygon(data=plate, aes(x=x,y=y), fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
|
| 1832 |
+
geom_point(aes(fill=TaggedPitchType), shape=21, size=4, color="black", stroke=.6, alpha=.95) +
|
| 1833 |
+
geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white") +
|
| 1834 |
+
scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", drop=FALSE, limits=apt) +
|
| 1835 |
+
coord_fixed() + labs(title="Strikes Lost - Catch Position") +
|
| 1836 |
+
theme_void() + theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
|
| 1837 |
+
legend.position="bottom")
|
| 1838 |
+
} else {
|
| 1839 |
+
make_zone_plot(d, "Strikes Lost")
|
| 1840 |
+
}
|
| 1841 |
})
|
| 1842 |
+
|
| 1843 |
+
output$recv_log_table <- renderDT({
|
| 1844 |
d <- recv_data() %>%
|
| 1845 |
filter(!is.na(frame)) %>%
|
| 1846 |
dplyr::select(Date, Inning, Pitcher, Batter, TaggedPitchType,
|
|
|
|
| 2187 |
ggplotly(p, tooltip="text")
|
| 2188 |
})
|
| 2189 |
|
| 2190 |
+
output$block_zone_plot <- renderPlot({
|
| 2191 |
bd <- block_data() %>%
|
| 2192 |
filter(!is.na(PlateLocSide) & !is.na(PlateLocHeight)) %>%
|
| 2193 |
mutate(row_num = row_number())
|
|
|
|
| 2194 |
view_mode <- input$block_view_mode
|
| 2195 |
+
if (nrow(bd) == 0) return(
|
| 2196 |
+
ggplot() + theme_void() + ggtitle("Blocking (Zone)") +
|
| 2197 |
+
theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
|
|
|
|
|
|
|
| 2198 |
if (view_mode == "Heatmap" && nrow(bd) >= 3) {
|
| 2199 |
+
return(make_zone_heatmap(bd, "Blocking (Zone) - Heatmap"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2200 |
}
|
| 2201 |
+
sl <- unique(bd$block_type)
|
| 2202 |
+
sv <- c("Block"=21, "PB/WP"=24)[sl]
|
| 2203 |
+
slb <- c("Block"="Block", "PB/WP"="PB/WP")[sl]
|
| 2204 |
+
tc <- bd %>% count(TaggedPitchType) %>% mutate(label=paste0(TaggedPitchType,": ",n))
|
| 2205 |
+
cl <- setNames(tc$label, tc$TaggedPitchType)
|
| 2206 |
+
ggplot(bd, aes(PlateLocSide, PlateLocHeight)) +
|
| 2207 |
+
geom_rect(data=bzr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
|
| 2208 |
+
fill=NA, color="gray50", linewidth=.6, linetype="dotted", inherit.aes=FALSE) +
|
| 2209 |
+
geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
|
| 2210 |
+
fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
|
| 2211 |
+
geom_polygon(data=hp_recv, aes(x=x,y=y),
|
| 2212 |
+
fill=NA, color="gray40", linewidth=.5, inherit.aes=FALSE) +
|
| 2213 |
+
geom_point(aes(fill=TaggedPitchType, shape=block_type), size=4, color="black", stroke=.6, na.rm=TRUE) +
|
| 2214 |
+
geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white", na.rm=TRUE) +
|
| 2215 |
+
scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", labels=cl) +
|
| 2216 |
+
scale_shape_manual(values=sv, name=NULL, labels=slb) +
|
| 2217 |
+
coord_equal(xlim=c(-2.5,2.5), ylim=c(-1,4.5)) +
|
| 2218 |
+
labs(title="Blocking (Zone)") + theme_void() +
|
| 2219 |
+
theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
|
| 2220 |
+
legend.position="bottom", legend.box="vertical",
|
| 2221 |
+
legend.spacing.y=unit(2,"pt"),
|
| 2222 |
+
legend.title=element_text(size=7, face="bold"),
|
| 2223 |
+
legend.text=element_text(size=6),
|
| 2224 |
+
legend.key.size=unit(.5,"lines")) +
|
| 2225 |
+
guides(fill=guide_legend(order=1, nrow=2, override.aes=list(size=3, shape=21)),
|
| 2226 |
+
shape=guide_legend(order=2, nrow=1, override.aes=list(size=3, fill="#4472C4")))
|
| 2227 |
})
|
| 2228 |
|
| 2229 |
output$block_log_table <- renderDT({
|