MTSU faculty at the assistant rank make up nearly a fourth of the full-time tenured or tenure-track staff at the university and would be the people most directly affected by a bill before the Tennessee General Assembly that would bar the state’s public higher education institutions from awarding tenure after July 1.
The first table summarizes counts and percentages for each faculty title. The second table and the graphic show counts and percentages after collapsing the titles by the three main faculty ranks: assistant, associate, and professor.
With rare exceptions, faculty at the assistant rank are untenured, while faculty at the associate and professor ranks are tenured.
| Salary Distribution by Title | ||
| Counts and column percentages | ||
| Title | Count | Percent |
|---|---|---|
| Professor | 293 | 43.4% |
| Associate Professor | 198 | 29.3% |
| Assistant Professor | 144 | 21.3% |
| Assistant Professor - 12 month | 12 | 1.8% |
| Associate Professor - 12 month | 11 | 1.6% |
| Professor - 12 month | 8 | 1.2% |
| Assistant Professor - 12 Month | 3 | 0.4% |
| Associate Professor - 12 Month | 3 | 0.4% |
| Associate Professor - 11 month | 1 | 0.1% |
| Professor - 11 month | 1 | 0.1% |
| Professor - 12 Month | 1 | 0.1% |
| Salary Distribution by Rank | ||
| Counts and column percentages | ||
| Rank | Count | Percent |
|---|---|---|
| Professor | 303 | 44.9% |
| Associate professor | 213 | 31.6% |
| Assistant professor | 159 | 23.6% |
Data for the table came from a search of the MTSU Employee Salary Database with “professor” added as a search criteria in the “Job Title” category. The R code shown below summarized the data into counts and percentages by rank.
###############################################################################
# SETUP: Install and load required packages
###############################################################################
# Ensure tidyverse is installed; install if missing, then load it
if (!requireNamespace("tidyverse", quietly = TRUE)) {
install.packages("tidyverse", dependencies = TRUE)
}
library(tidyverse)
# Ensure gt is installed; install if missing, then load it
if (!requireNamespace("gt", quietly = TRUE)) {
install.packages("gt", dependencies = TRUE)
}
library(gt)
# Ensure plotly is installed; install if missing, then load it
if (!requireNamespace("plotly", quietly = TRUE)) {
install.packages("plotly", dependencies = TRUE)
}
library(plotly)
# (Optional) ensure 'scales' explicitly if you prefer; it's pulled via ggplot2
# if (!requireNamespace("scales", quietly = TRUE)) {
# install.packages("scales", dependencies = TRUE)
# }
# library(scales)
###############################################################################
# INPUT: Read source data
###############################################################################
Data <- read_csv("SalaryDatabase.csv")
###############################################################################
# TRANSFORM (Title-level): Summarize counts and compute column percentages
###############################################################################
DataSummary <- Data %>%
count(Title, name = "Count") %>%
mutate(
Percent = Count / sum(Count),
PercentLabel = scales::percent(Percent, accuracy = 0.1)
) %>%
arrange(desc(Count))
###############################################################################
# OUTPUT (Title-level Data Frame): Select and rename for presentation
###############################################################################
DataSummary <- DataSummary %>%
select(Title, Count, PercentLabel) %>%
rename(Percent = PercentLabel)
###############################################################################
# OUTPUT (Title-level Table): Create a nicely formatted gt table
###############################################################################
DataSummaryTable <- DataSummary %>%
gt() %>%
tab_header(
title = md("**Salary Distribution by Title**"),
subtitle = "Counts and column percentages"
) %>%
cols_label(
Title = "Title",
Count = "Count",
Percent = "Percent"
) %>%
cols_align(
align = "right",
columns = c(Count, Percent)
) %>%
tab_options(
table.font.size = px(14),
data_row.padding = px(6)
)
###############################################################################
# TRANSFORM (Rank-level): Add Rank via case_when and summarize
###############################################################################
# Create Rank from Title:
# - Titles containing "assistant" → "Assistant professor"
# - Titles containing "associate" → "Associate professor"
# - Everything else → "Professor"
# Matching is case-insensitive and robust to variations like "Assistant Professor"
Data <- Data %>%
mutate(
Title_lower = stringr::str_to_lower(Title),
Rank = case_when(
stringr::str_detect(Title_lower, "assistant") ~ "Assistant professor",
stringr::str_detect(Title_lower, "associate") ~ "Associate professor",
TRUE ~ "Professor"
)
) %>%
select(-Title_lower) # remove helper column
# Summarize by Rank with counts and column percentages
RankSummary <- Data %>%
count(Rank, name = "Count") %>%
mutate(
Percent = Count / sum(Count),
PercentLabel = scales::percent(Percent, accuracy = 0.1)
) %>%
arrange(desc(Count)) %>%
select(Rank, Count, Percent = PercentLabel)
###############################################################################
# OUTPUT (Rank-level Table): Create gt table of counts and column percentages
###############################################################################
RankSummaryTable <- RankSummary %>%
gt() %>%
tab_header(
title = md("**Salary Distribution by Rank**"),
subtitle = "Counts and column percentages"
) %>%
cols_label(
Rank = "Rank",
Count = "Count",
Percent = "Percent"
) %>%
cols_align(
align = "right",
columns = c(Count, Percent)
) %>%
tab_options(
table.font.size = px(14),
data_row.padding = px(6)
)
###############################################################################
# VISUAL (Rank-level): Plotly column chart of RankSummary$Count
###############################################################################
# Order Rank by Count for a clean left-to-right display (optional)
RankSummary_plot_data <- RankSummary %>%
mutate(Rank = factor(Rank, levels = Rank[order(Count, decreasing = TRUE)]))
# Build interactive bar (column) chart
RankSummaryPlot <- plot_ly(
data = RankSummary_plot_data,
x = ~Rank,
y = ~Count,
type = "bar",
text = ~paste0("Count: ", Count, "<br>Percent: ", Percent),
hoverinfo = "text",
marker = list(color = "#4472C4")
) %>%
layout(
title = list(text = "<b>MTSU Faculty Counts by Rank</b>"),
xaxis = list(title = "Rank"),
yaxis = list(title = "Count"),
bargap = 0.2
)
# Add a source note with a clickable link that opens in a new tab
RankSummaryPlot <- RankSummaryPlot %>%
layout(
annotations = list(
list(
x = 0, y = -0.18, xref = "paper", yref = "paper",
xanchor = "left", yanchor = "top",
showarrow = FALSE,
align = "left",
text = paste0(
"Data source: ",
"<a href='https://w1.mtsu.edu/hrs/salary/employee-salary-database.php' target='_blank'>",
"MTSU Employee Salary Database</a>."
),
font = list(size = 12, color = "#555555")
)
),
margin = list(b = 90) # extra bottom margin so the note isn't clipped
)
# View interactive outputs (uncomment when running interactively)
DataSummaryTable
RankSummaryTable
RankSummaryPlot