Analyzing WIDA ACCESS Scores and Allocating Support with an Equity Lens

library(readxl) # import excel data set
library(tidyverse) # load core packages for data visualisation (dplyr:for data manipulation, tidyr:for data tidying, readr:for data import, purrr:for functional programming, tibble:for tibbles, a modern re-imagining of data frames, stringr:for strings, forcats:for factors)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.2     ✔ purrr   1.0.1
## ✔ tibble  3.2.1     ✔ dplyr   1.1.2
## ✔ tidyr   1.3.0     ✔ stringr 1.5.0
## ✔ readr   2.1.4     ✔ forcats 1.0.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(moonBook) # For Donuts Chart
library(webr) # For Donuts Chart
library(plyr)
## ------------------------------------------------------------------------------
## You have loaded plyr after dplyr - this is likely to cause problems.
## If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
## library(plyr); library(dplyr)
## ------------------------------------------------------------------------------
## 
## Attaching package: 'plyr'
## 
## The following objects are masked from 'package:dplyr':
## 
##     arrange, count, desc, failwith, id, mutate, rename, summarise,
##     summarize
## 
## The following object is masked from 'package:purrr':
## 
##     compact
library(plotly)
## 
## Attaching package: 'plotly'
## 
## The following objects are masked from 'package:plyr':
## 
##     arrange, mutate, rename, summarise
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following object is masked from 'package:graphics':
## 
##     layout
library(lubridate)
## 
## Attaching package: 'lubridate'
## 
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(ggplot2)
library(kableExtra)
## Warning in !is.null(rmarkdown::metadata$output) && rmarkdown::metadata$output
## %in% : 'length(x) = 2 > 1' in coercion to 'logical(1)'
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(reshape2)
## 
## Attaching package: 'reshape2'
## 
## The following object is masked from 'package:tidyr':
## 
##     smiths
library(rmdformats)
library(igraph)
## 
## Attaching package: 'igraph'
## 
## The following objects are masked from 'package:lubridate':
## 
##     %--%, union
## 
## The following object is masked from 'package:plotly':
## 
##     groups
## 
## The following objects are masked from 'package:dplyr':
## 
##     as_data_frame, groups, union
## 
## The following objects are masked from 'package:purrr':
## 
##     compose, simplify
## 
## The following object is masked from 'package:tidyr':
## 
##     crossing
## 
## The following object is masked from 'package:tibble':
## 
##     as_data_frame
## 
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## 
## The following object is masked from 'package:base':
## 
##     union
library(treemap)
library(chatgpt)
library(httr)
## 
## Attaching package: 'httr'
## 
## The following object is masked from 'package:plotly':
## 
##     config
library(stringr)

Objective

Using an equity lens, assess student performance based on 2022 and 2023 WIDA ACCESS Scores and make recommendations on allocating support.

#Total Negative

total_neg_growth_grade <- Equity_Data_Negative %>%
  group_by(Grade, Growth) %>%
  summarise(
    num_grade = n(),                         # Calculate the number of applications
    percentage_of_grade = num_grade / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
total_neg_growth_grade_level <- Equity_Data_Negative %>%
  group_by(Grade_Level, Growth) %>%
  summarise(
    num_grade = n(),                         # Calculate the number of applications
    percentage_of_grade = num_grade / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'Grade_Level'. You can override using the
## `.groups` argument.
total_neg_growth_race <- Equity_Data_Negative %>%
  group_by(Race, Growth) %>%
  summarise(
    num_race = n(),                         # Calculate the number of applications
    percentage_of_race = num_race / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'Race'. You can override using the
## `.groups` argument.
total_neg_growth_program <- Equity_Data_Negative %>%
  group_by(Program, Growth) %>%
  summarise(
    num_program = n(),                         # Calculate the number of applications
    percentage_of_program = num_program / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'Program'. You can override using the
## `.groups` argument.
total_neg_growth_lang <- Equity_Data_Negative %>%
  group_by(First_Lang, Growth) %>%
  summarise(
    num_lang = n(),                         # Calculate the number of applications
    percentage_of_lang = num_lang / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'First_Lang'. You can override using the
## `.groups` argument.
total_neg_growth_sn <- Equity_Data_Negative %>%
  group_by(SN, Growth) %>%
  summarise(
    num_sn = n(),                         # Calculate the number of applications
    percentage_of_sn = num_sn / nrow(Equity_Data) * 100  # Calculate the percentage
  )
## `summarise()` has grouped output by 'SN'. You can override using the `.groups`
## argument.
# Calculate the average negative score by School
explore_school <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  group_by(School) %>%
  summarise(average_negative = mean(Difference))

explore_school_10 <- explore_school %>%  
  top_n(10)
## Selecting by average_negative
# Calculate the average negative score by Program
explore_program <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  group_by(Program) %>%
  summarise(average_negative = mean(Difference)) 

explore_program_10 <- explore_program %>%
  top_n(10)
## Selecting by average_negative
# Calculate the average negative score by First_Lang
explore_first_lang <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  group_by(First_Lang) %>%
  summarise(average_negative = mean(Difference)) 

explore_first_lang_10 <- explore_first_lang %>%
  top_n(10)
## Selecting by average_negative
# Calculate the average negative score by Grade
explore_grade <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference)) 

explore_grade_10 <- explore_grade %>%
  top_n(10)
## Selecting by average_negative
# Calculate the average negative score by Race
explore_race <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  group_by(Race) %>%
  summarise(average_negative = mean(Difference))

Overview

Boston Public Schools Equity Data set contains:

8,618 Students

113 Schools

  • Elementary Schools 75
  • Middle Schools 70
  • High Schools 35

Over 1/2 of the students population is in Elementary School

plot_ly(total_grade, labels = ~Grade_Level, values = ~num_grade, type = "pie") %>%
  layout(title = "Grade Level Distribution")

Race

Hispanic students account for 68% of the data set.

plot_ly(total_race, labels = ~Race, values = ~num_race, type = "pie") %>%
  layout(title = "Race Distribution")

Special Needs

Large majority of data set is students in General Education, 76%. R1 ‘0-90 minutes of service provided in full or partial inclusion setting’ is the largest Special Needs code in the data set.

plot_ly(total_sn, labels = ~SN, values = ~num_sn, type = "pie") %>%
  layout(title = "Special Needs Distribution")

Program

Almost 1/2 of the students are in General Education, while the second and third largest Program are ‘SEI - Spanish’ & ‘SEI - Multilingual’.

plot_ly(total_program, labels = ~Program, values = ~num_program, type = "pie") %>%
  layout(title = "Program Distribution")

First Language

Spanish is the largest First Language in this data set, followed by English and Cape Verdean.

plot_ly(total_lang, labels = ~First_Lang, values = ~num_lang, type = "pie") %>%
  layout(title = "First Language Distribution")

Definition of Equity

Equity means ensuring that every student has access to the necessary resources and support to reach their full potential, regardless of their race, language background, or educational program. It involves recognizing and addressing systemic inequalities to create an inclusive and fair educational environment for all students. The Equity Lens I used was to examine students who had negative growth and compare them general education students vs our special education students, looking for disparities when exploring Program, Race and First Language.

Methodology

Criteria for Comparison

Highlight of Analysis

Overall Growth by Grade

Majority of Growth has been positive.

plot_ly(total_growth_grade, x = ~Grade, y = ~num_grade, color = ~Growth, type = "bar") %>%
  layout(
    title = "Growth by Grade",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Number of Students"),
    barmode = "group"
  )

Negative Growth by Grade

Equity Lens focuses on students who had Negative or No growth from SY22 to SY23

plot_ly(total_neg_growth_grade, labels = ~Grade, values = ~num_grade, type = "pie") %>%
  layout(title = "Grade Level Distribution of Negative Scores")

Negative Growth by Race

plot_ly(total_neg_growth_race, labels = ~Race, values = ~num_race, type = "pie") %>%
  layout(title = "Race Distribution of Negative Scores")

Negative Growth by Program

plot_ly(total_neg_growth_program, labels = ~Program, values = ~num_program, type = "pie") %>%
  layout(title = "Program Distribution of Negative Scores")

Negative Growth by First Language

plot_ly(total_neg_growth_lang, labels = ~First_Lang, values = ~num_lang, type = "pie") %>%
  layout(title = "First Language Distribution of Negative Scores")

Negative Growth by Special Needs

## Special Needs
plot_ly(total_neg_growth_sn, labels = ~SN, values = ~num_sn, type = "pie") %>%
  layout(title = "Special Needs Distribution of Negative Scores")

Variations in performance based on Grade Level and Program

# Divide the dataset by General Education and Special Needs
gen_ed <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  filter(SN == "General Education")

special_needs <- Equity_Data %>%
  filter(Growth == "Negative") %>%
  filter(SN != "General Education")

# Explore General Education with by School
explore_gen_ed <- gen_ed %>%
  group_by(Grade_Level, School) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade_Level'. You can override using the
## `.groups` argument.
explore_gen_ed_10 <- explore_gen_ed %>%
  top_n(10)
## Selecting by average_negative
total_gen_ed_school <- gen_ed %>%
  group_by(Grade_Level, School) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade_Level'. You can override using the
## `.groups` argument.
data_subset <- subset(total_gen_ed_school, rank(-average_negative, ties.method = "min") <= 10)

# Explore Special Needs 
explore_special_needs <- special_needs %>%
  group_by(Grade_Level, School) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade_Level'. You can override using the
## `.groups` argument.
explore_special_needs_10 <- explore_special_needs %>%
  top_n(10)
## Selecting by average_negative
total_special_needs_school <- special_needs %>%
  group_by(Grade_Level, School) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade_Level'. You can override using the
## `.groups` argument.
data_subset_sn <- subset(total_special_needs_school, rank(-average_negative, ties.method = "min") <= 10) 

# Divide General Education by Grade_Level
gen_ed_elementary <- gen_ed %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

gen_ed_elementary_10 <- gen_ed_elementary %>%
  top_n(10)
## Selecting by average_negative
gen_ed_middle <- gen_ed %>%
  filter(Grade_Level == "Middle")  %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

gen_ed_middle_10 <- gen_ed_middle %>%
  top_n(10)
## Selecting by average_negative
gen_ed_high <- gen_ed %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

gen_ed_high_10 <- gen_ed_high %>%
  top_n(10)
## Selecting by average_negative
# Divide Special Needs by Grade_Level
special_needs_elementary <- special_needs %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

special_needs_elementary_10 <- special_needs_elementary %>%
  top_n(10)
## Selecting by average_negative
special_needs_elementary_race <- special_needs %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade, Race) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_elementary_program <- special_needs %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade, Program) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_elementary_sn <- special_needs %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade, SN) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_elementary_lang <- special_needs %>%
  filter(Grade_Level == "Elementary") %>%
  group_by(Grade, First_Lang) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_middle <- special_needs %>%
  filter(Grade_Level == "Middle") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

special_needs_middle_10 <- special_needs_middle %>%
  top_n(10)
## Selecting by average_negative
special_needs_middle_race <- special_needs %>%
  filter(Grade_Level == "Middle") %>%
  group_by(Grade, Race) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_middle_program <- special_needs %>%
  filter(Grade_Level == "Middle") %>%
  group_by(Grade, Program) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_middle_sn <- special_needs %>%
  filter(Grade_Level == "Middle") %>%
  group_by(Grade, SN) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_middle_lang <- special_needs %>%
  filter(Grade_Level == "Middle") %>%
  group_by(Grade, First_Lang) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_high <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

special_needs_high_10 <- special_needs_high %>%
  top_n(10)
## Selecting by average_negative
special_needs_high_race <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade, Race) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_high_program <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade, Program) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_high_sn <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade, SN) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_high_lang <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade, First_Lang) %>%
  summarise(average_negative = mean(Difference))
## `summarise()` has grouped output by 'Grade'. You can override using the
## `.groups` argument.
special_needs_high <- special_needs %>%
  filter(Grade_Level == "High") %>%
  group_by(Grade) %>%
  summarise(average_negative = mean(Difference))

ge1 <- plot_ly(data_subset, x = ~Grade_Level, y = ~average_negative, color = ~School, type = "bar") %>%
  layout(
    title = "Schools With Highest Neagtive Growth",
    xaxis = list(title = "Grade Level"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

sn1 <- plot_ly(data_subset_sn, x = ~Grade_Level, y = ~average_negative, color = ~School, type = "bar") %>%
  layout(
    title = "Schools With Highest Neagtive Growth",
    xaxis = list(title = "Grade Level"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

General Education vs Special Needs

p <- subplot(ge1, sn1, nrows = 1)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
p

Elementary

Special Needs

# Grade
special_needs_elementary_plot <- plot_ly(special_needs_elementary, x = ~Grade, y = ~average_negative, color = ~Grade, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Grade",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Race

special_needs_elementary_race_plot <- plot_ly(special_needs_elementary_race, x = ~Grade, y = ~average_negative, color = ~Race, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Race",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Program
special_needs_elementary_program_plot <- plot_ly(special_needs_elementary_program, x = ~Grade, y = ~average_negative, color = ~Program, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Program",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# SN
special_needs_elementary_sn_plot <- plot_ly(special_needs_elementary_sn, x = ~Grade, y = ~average_negative, color = ~SN, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by SN",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# First Lang

special_needs_elementary_lang_plot <- plot_ly(special_needs_elementary_lang, x = ~Grade, y = ~average_negative, color = ~First_Lang, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by First Language",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

Elem_plots <- subplot(special_needs_elementary_plot, special_needs_elementary_race_plot,special_needs_elementary_program_plot, special_needs_elementary_lang_plot, nrows = 4)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
Elem_plots

Middle

# Grade
special_needs_middle_plot <- plot_ly(special_needs_middle, x = ~Grade, y = ~average_negative, color = ~Grade, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Grade",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Race

special_needs_middle_race_plot <- plot_ly(special_needs_middle_race, x = ~Grade, y = ~average_negative, color = ~Race, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Race",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Program
special_needs_middle_program_plot <- plot_ly(special_needs_middle_program, x = ~Grade, y = ~average_negative, color = ~Program, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Program",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# SN
special_needs_middle_sn_plot <- plot_ly(special_needs_middle_sn, x = ~Grade, y = ~average_negative, color = ~SN, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by SN",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# First Lang

special_needs_middle_lang_plot <- plot_ly(special_needs_middle_lang, x = ~Grade, y = ~average_negative, color = ~First_Lang, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by First Language",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

Mid_plots <- subplot(special_needs_middle_plot, special_needs_middle_race_plot,special_needs_middle_program_plot, special_needs_middle_lang_plot, nrows = 4)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
Mid_plots

High

# Grade
special_needs_high_plot <- plot_ly(special_needs_high, x = ~Grade, y = ~average_negative, color = ~Grade, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Grade",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Race

special_needs_high_race_plot <- plot_ly(special_needs_high_race, x = ~Grade, y = ~average_negative, color = ~Race, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Race",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# Program
special_needs_high_program_plot <- plot_ly(special_needs_high_program, x = ~Grade, y = ~average_negative, color = ~Program, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by Program",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# SN
special_needs_high_sn_plot <- plot_ly(special_needs_high_sn, x = ~Grade, y = ~average_negative, color = ~SN, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by SN",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

# First Lang

special_needs_high_lang_plot <- plot_ly(special_needs_high_lang, x = ~Grade, y = ~average_negative, color = ~First_Lang, type = "bar") %>%
  layout(
    title = "Average Neagtive Growth by First Language",
    xaxis = list(title = "Grade"),
    yaxis = list(title = "Average Growth"),
    barmode = "group"
  )

High_plots <- subplot(special_needs_high_plot, special_needs_high_race_plot,special_needs_high_program_plot, special_needs_high_lang_plot, nrows = 4)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
High_plots

Recommendations and Rationale

Recommendation 1:

Increase targeted language support for students in grades 1, 6 & 9, as they demonstrated lower average scores compared to other grades.

Recommendation 2:

Provide additional resources and interventions for students in the BLM, BLC, and BLH programs, as they showed lower average scores compared to students in other programs.

Recommendation 3:

Develop individualized support plans for students with disabilities to ensure their specific language needs are met.

Questions and Further Considerations