FORSCHUNGSFRAGE:

Gibt es einen Zusammenhang zwischen der Klassengröße und dem Anteil der

Schüler mit Förderbedarf in den Kreisen Schleswig-Holsteins?

HYPOTHESE:

H0: Es gibt keinen linearen Zusammenhang zwischen Klassengröße und Förderanteil

H1: Größere Klassen führen zu einem geringeren Anteil von Schülern mit Förderbedarf

Die Nullhypothese (H0) geht davon aus, dass kein statistisch signifikanter linearer Zusammenhang besteht. Die Alternativhypothese (H1) nimmt an, dass größere Klassen mit einem niedrigeren Förderbedarf einhergehen

#DATEN EINLESEN UND VORBEREITEN/BEREINIGEN

# Pakete laden
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readr)
library(ggplot2)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(corrr)
library(broom)
library(ggrepel)
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
# Daten einlesen
klassengroesse <- read_delim("/Users/yaren/Downloads/durchschnittliche_klassengroesse.csv", 
                            delim = ";", 
                             locale = locale(encoding = "latin1"),
                              show_col_types = FALSE)
                                                                                        
foerderbedarf <- read_csv("/Users/yaren/Downloads/foerderbedarf.csv",
                           locale = locale(encoding = "UTF-8"),
                           show_col_types = FALSE)
## New names:
## • `` -> `...1`

ERSTE EXPLORATION: DATENSTRUKTUR ANZEIGEN

str(klassengroesse)
## spc_tbl_ [72 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ Kreis/ kreisfreie Stadt: chr [1:72] "Kiel" NA NA NA ...
##  $ Schule                 : chr [1:72] "Ellerbeker Schule" "Theodor-Storm-Gemeinschaftsschule" "Lilli-Martius-Schule" "Friedrich-Junge-Gemeinschaftsschule Schreventeich/Wik" ...
##  $ Ort                    : chr [1:72] "Kiel" "Kiel" "Kiel" "Kiel" ...
##  $ SuS (inkl.DaZ)         : num [1:72] 198 119 236 97 133 210 228 158 203 163 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   `Kreis/ kreisfreie Stadt` = col_character(),
##   ..   Schule = col_character(),
##   ..   Ort = col_character(),
##   ..   `SuS (inkl.DaZ)` = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>
#spc_tbl_ [72 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
#$ Kreis/ kreisfreie Stadt: chr [1:72] "Kiel" NA NA NA ...
# $ Schule                 : chr [1:72] "Ellerbeker Schule" "Theodor-Storm-Gemeinschaftsschule" "Lilli-Martius-Schule" "Friedrich-Junge-Gemeinschaftsschule Schreventeich/Wik" ...
 # $ Ort                    : chr [1:72] "Kiel" "Kiel" "Kiel" "Kiel" ...
 # $ SuS (inkl.DaZ)         : num [1:72] 198 119 236 97 133 210 228 158 203 163 ...
 # - attr(*, "spec")=
 # .. cols(
 # ..   `Kreis/ kreisfreie Stadt` = col_character(),
 # ..   Schule = col_character(),
 # ..   Ort = col_character(),
 # ..   `SuS (inkl.DaZ)` = col_double()
 # .. )
 # - attr(*, "problems")=<externalptr> 
 head(klassengroesse)
## # A tibble: 6 × 4
##   `Kreis/ kreisfreie Stadt` Schule                        Ort   `SuS (inkl.DaZ)`
##   <chr>                     <chr>                         <chr>            <dbl>
## 1 Kiel                      Ellerbeker Schule             Kiel               198
## 2 <NA>                      Theodor-Storm-Gemeinschaftss… Kiel               119
## 3 <NA>                      Lilli-Martius-Schule          Kiel               236
## 4 <NA>                      Friedrich-Junge-Gemeinschaft… Kiel                97
## 5 <NA>                      Klaus-Groth-Schule mit Grund… Kiel               133
## 6 <NA>                      Hermann-Löns-Schule           Kiel               210
# A tibble: 6 × 4
 # `Kreis/ kreisfreie Stadt` Schule                                Ort   `SuS (inkl.DaZ)`
#  <chr>                     <chr>                                 <chr>            # <dbl>
# 1 Kiel                      Ellerbeker Schule                     Kiel               # 198
# 2 NA                        Theodor-Storm-Gemeinschaftsschule     Kiel               # 119
# 3 NA                        Lilli-Martius-Schule                  Kiel               # 236
# 4 NA                        Friedrich-Junge-Gemeinschaftsschule … Kiel                97
# 5 NA                        Klaus-Groth-Schule mit Grundschulteil Kiel               133
# 6 NA                        Hermann-Löns-Schule                   Kiel               210
summary(klassengroesse)
##  Kreis/ kreisfreie Stadt    Schule              Ort            SuS (inkl.DaZ) 
##  Length:72               Length:72          Length:72          Min.   :  1.0  
##  Class :character        Class :character   Class :character   1st Qu.:150.2  
##  Mode  :character        Mode  :character   Mode  :character   Median :202.0  
##                                                                Mean   :205.2  
##                                                                3rd Qu.:261.5  
##                                                                Max.   :458.0
 # Kreis/ kreisfreie Stadt    Schule              Ort            SuS (inkl.DaZ) 
 # Length:72               Length:72          Length:72          Min.   :  1.0  
 # Class :character        Class :character   Class :character   1st Qu.:150.2  
#  Mode  :character        Mode  :character   Mode  :character   Median :202.0  
#                                                               mean   :205.2  
 #                                                               3rd Qu.:261.5  
#                                                               Max.   :458.0 
klassengroesse[!complete.cases(klassengroesse), ]
## # A tibble: 58 × 4
##    `Kreis/ kreisfreie Stadt` Schule                       Ort   `SuS (inkl.DaZ)`
##    <chr>                     <chr>                        <chr>            <dbl>
##  1 <NA>                      Theodor-Storm-Gemeinschafts… Kiel               119
##  2 <NA>                      Lilli-Martius-Schule         Kiel               236
##  3 <NA>                      Friedrich-Junge-Gemeinschaf… Kiel                97
##  4 <NA>                      Klaus-Groth-Schule mit Grun… Kiel               133
##  5 <NA>                      Hermann-Löns-Schule          Kiel               210
##  6 <NA>                      Max-Tau-Schule               Kiel               228
##  7 <NA>                      St. Jürgen Grund- und Gemei… Lübe…              203
##  8 <NA>                      Julius-Leber-Schule          Lübe…              163
##  9 <NA>                      Schule Tremser Teich         Lübe…              263
## 10 <NA>                      Trave-Grund- und Gemeinscha… Lübe…              151
## # ℹ 48 more rows
# A tibble: 58 × 4
#    `Kreis/ kreisfreie Stadt` Schule                               Ort   `SuS (inkl.DaZ)`
#   <chr>                     <chr>                                <chr>            <dbl>
# 1 NA                        Theodor-Storm-Gemeinschaftsschule    Kiel               119
# 2 NA                        Lilli-Martius-Schule                 Kiel               236
# 3 NA                        Friedrich-Junge-Gemeinschaftsschule… Kiel                97
 # 4 NA                        Klaus-Groth-Schule mit Grundschulte… Kiel               133
 # 5 NA                        Hermann-Löns-Schule                  Kiel               210
#  6 NA                        Max-Tau-Schule                       Kiel               228
# 7 NA                        St. Jürgen Grund- und Gemeinschafts… Lübe…              203
# 8 NA                        Julius-Leber-Schule                  Lübe…              163
# 9 NA                        Schule Tremser Teich                 Lübe…              263
# 10 NA                        Trave-Grund- und Gemeinschaftsschule Lübe…              151
# ℹ 48 more rows
# ℹ Use `print(n = ...)` to see more rows
str(foerderbedarf)
## spc_tbl_ [160 × 7] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ ...1             : chr [1:160] "Schleswig-Holstein" "Schleswig-Holstein" "Schleswig-Holstein" "Schleswig-Holstein" ...
##  $ Förderschwerpunkt: chr [1:160] "Lernen" "Sprache" "Emotionale und soziale Entwicklung" "Geistige Entwicklung" ...
##  $ Jgst. 1          : num [1:160] 31 217 27 57 45 25 20 24 1 447 ...
##  $ Jgst. 2          : num [1:160] 67 299 54 89 57 26 13 52 7 664 ...
##  $ Jgst. 3          : num [1:160] 552 74 79 62 65 35 16 47 3 933 ...
##  $ Jgst. 4          : num [1:160] 895 56 116 93 57 ...
##  $ gesamt           : num [1:160] 1545 646 276 301 224 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   ...1 = col_character(),
##   ..   Förderschwerpunkt = col_character(),
##   ..   `Jgst. 1` = col_double(),
##   ..   `Jgst. 2` = col_double(),
##   ..   `Jgst. 3` = col_double(),
##   ..   `Jgst. 4` = col_double(),
##   ..   gesamt = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>
# spc_tbl_ [160 × 7] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
#  $ ...1             : chr [1:160] "Schleswig-Holstein" "Schleswig-Holstein" "Schleswig-Holstein" "Schleswig-Holstein" ...
#  $ Förderschwerpunkt: chr [1:160] "Lernen" "Sprache" "Emotionale und soziale Entwicklung" "Geistige Entwicklung" ...
# $ Jgst. 1          : num [1:160] 31 217 27 57 45 25 20 24 1 447 ...
# $ Jgst. 2          : num [1:160] 67 299 54 89 57 26 13 52 7 664 ...
# $ Jgst. 3          : num [1:160] 552 74 79 62 65 35 16 47 3 933 ...
# $ Jgst. 4          : num [1:160] 895 56 116 93 57 ...
# $ gesamt           : num [1:160] 1545 646 276 301 224 ...
# - attr(*, "spec")=
#  .. cols(
#  ..   ...1 = col_character(),
#  ..   Förderschwerpunkt = col_character(),
#  ..   `Jgst. 1` = col_double(),
#  ..   `Jgst. 2` = col_double(),
#  ..   `Jgst. 3` = col_double(),
#  ..   `Jgst. 4` = col_double(),
#  ..   gesamt = col_double()
#  .. )
# - attr(*, "problems")=<externalptr> 
head(foerderbedarf)
## # A tibble: 6 × 7
##   ...1          Förderschwerpunkt `Jgst. 1` `Jgst. 2` `Jgst. 3` `Jgst. 4` gesamt
##   <chr>         <chr>                 <dbl>     <dbl>     <dbl>     <dbl>  <dbl>
## 1 Schleswig-Ho… Lernen                   31        67       552       895   1545
## 2 Schleswig-Ho… Sprache                 217       299        74        56    646
## 3 Schleswig-Ho… Emotionale und s…        27        54        79       116    276
## 4 Schleswig-Ho… Geistige Entwick…        57        89        62        93    301
## 5 Schleswig-Ho… Körperliche und …        45        57        65        57    224
## 6 Schleswig-Ho… Hören                    25        26        35        39    125
# A tibble: 6 × 7
#  ...1               Förderschwerpunkt    `Jgst. 1` `Jgst. 2` `Jgst. 3` `Jgst. 4` gesamt
#  <chr>              <chr>                    <dbl>     <dbl>     <dbl>     <dbl>  <dbl>
# 1 Schleswig-Holstein Lernen                      31        67       552       895   1545
# 2 Schleswig-Holstein Sprache                    217       299        74        56    646
# 3 Schleswig-Holstein Emotionale und sozi…        27        54        79       116    276
# 4 Schleswig-Holstein Geistige Entwicklung        57        89        62        93    301
# 5 Schleswig-Holstein Körperliche und mot…        45        57        65        57    224
# 6 Schleswig-Holstein Hören                       25        26        35        39    125
summary(foerderbedarf)
##      ...1           Förderschwerpunkt     Jgst. 1          Jgst. 2     
##  Length:160         Length:160         Min.   :  0.00   Min.   :  0.0  
##  Class :character   Class :character   1st Qu.:  0.00   1st Qu.:  1.0  
##  Mode  :character   Mode  :character   Median :  2.00   Median :  3.0  
##                                        Mean   : 11.18   Mean   : 16.6  
##                                        3rd Qu.:  6.25   3rd Qu.:  9.0  
##                                        Max.   :447.00   Max.   :664.0  
##     Jgst. 3          Jgst. 4            gesamt      
##  Min.   :  0.00   Min.   :   0.00   Min.   :   0.0  
##  1st Qu.:  1.00   1st Qu.:   1.00   1st Qu.:   5.0  
##  Median :  3.00   Median :   5.00   Median :  15.0  
##  Mean   : 23.32   Mean   :  34.10   Mean   :  85.2  
##  3rd Qu.: 16.00   3rd Qu.:  20.25   3rd Qu.:  68.0  
##  Max.   :933.00   Max.   :1364.00   Max.   :3408.0
#     ...1           Förderschwerpunkt     Jgst. 1          Jgst. 2     
# Length:160         Length:160         Min.   :  0.00   Min.   :  0.0  
# Class :character   Class :character   1st Qu.:  0.00   1st Qu.:  1.0  
# Mode  :character   Mode  :character   Median :  2.00   Median :  3.0  
 #                                      mean   : 11.18   mean   : 16.6  
#                                       3rd Qu.:  6.25   3rd Qu.:  9.0  
#                                       Max.   :447.00   Max.   :664.0  
 #   Jgst. 3          Jgst. 4            gesamt      
# Min.   :  0.00   Min.   :   0.00   Min.   :   0.0  
# 1st Qu.:  1.00   1st Qu.:   1.00   1st Qu.:   5.0  
# Median :  3.00   Median :   5.00   Median :  15.0  
# mean   : 23.32   mean   :  34.10   mean   :  85.2  
# 3rd Qu.: 16.00   3rd Qu.:  20.25   3rd Qu.:  68.0  
# Max.   :933.00   Max.   :1364.00   Max.   :3408.0 

Hier wird die Struktur der Klassengrößendaten inspiziert: Anzahl der Variablen, Datentypen und erste Zeilen. Fehlende Werte werden identifiziert, vor allem bei der Kreisbezeichnung.

2.1 KLASSENGRÖßENDATEN BEREINIGEN

klassengroesse_clean <- klassengroesse %>%
  # Gesamtsumme herausfiltern
  filter(!(`Kreis/ kreisfreie Stadt` %in% c("Gesamt SH", NA) & is.na(Schule))) %>%
  # Fehlende Kreisnamen ergänzen (forward fill)
  fill(`Kreis/ kreisfreie Stadt`, .direction = "down") %>%
  # Spalten umbenennen für bessere Handhabung
  rename(
    kreis = `Kreis/ kreisfreie Stadt`,
    schule = `Schule`,
    ort = `Ort`,
    schueler_anzahl = `SuS (inkl.DaZ)`
  ) %>%
  # Schülerzahl als numerisch definieren und gültige Daten filtern
  mutate(schueler_anzahl = as.numeric(schueler_anzahl)) %>%
  filter(!is.na(schueler_anzahl), schueler_anzahl > 0, !is.na(kreis))
                     1
## [1] 1

Es wurden Gesamtsummen und leere Zeilen entfernt. Fehlende Kreisnamen wurden mit dem Wert der vorherigen Zeile ausgefüllt und Variablen wurden umbenannt.Zudem wurden ungültige und Nullwerte herausgefiltert.

2.2 FÖRDERBEDARFDATEN BEREINIGEN

foerderbedarf_clean <- foerderbedarf %>%
  # Erste Spalte korrekt benennen
  rename(region = 1) %>%
  # Nur Kreisdaten (ohne Gesamtsumme für Schleswig-Holstein)
  filter(region != "Schleswig-Holstein") %>%
  # Nur Zeilen mit "Alle Förderschwerpunkte" für Gesamtübersicht
  filter(Förderschwerpunkt == "Alle Förderschwerpunkte") %>%
  # Relevante Spalten auswählen
  select(region, gesamt) %>%
  rename(kreis = region, foerderbedarf_gesamt = gesamt)

Hier wird nur der Gesamt-Förderbedarf pro Kreis extrahiert – unabhängig vom Förderschwerpunkt – und auf Kreisnamen reduziert.

Daten bereinigen

klassengroesse_kreis <- klassengroesse_clean %>%
  group_by(kreis) %>%
  summarise(
    anzahl_schulen = n(),
    schueler_gesamt_kreis = sum(schueler_anzahl, na.rm = TRUE),
    durchschnitt_schueler_pro_schule = mean(schueler_anzahl, na.rm = TRUE),
    median_schueler_pro_schule = median(schueler_anzahl, na.rm = TRUE),
    .groups = 'drop'
  ) %>%
  mutate(
    geschaetzte_klassen_pro_schule = durchschnitt_schueler_pro_schule / 25,
    geschaetzte_klassengroesse = ifelse(
      geschaetzte_klassen_pro_schule >= 1, 
      durchschnitt_schueler_pro_schule / round(geschaetzte_klassen_pro_schule),
      durchschnitt_schueler_pro_schule
    )
  )
# Datensätze verknüpfen
analyse_daten <- klassengroesse_kreis %>%
 inner_join(foerderbedarf_clean, by = "kreis") %>%
 # Förderanteil berechnen
mutate(
  foerderanteil_prozent = (foerderbedarf_gesamt / schueler_gesamt_kreis) * 100
  ) %>%
  # Nur vollständige Fälle behalten
   filter(!is.na(foerderanteil_prozent), !is.na(geschaetzte_klassengroesse))
# print("\n=== FINALE ANALYSEDATEN ===")

Die beiden Datensätze werden auf Kreisebene aggregiert und verknüpft. Es werden geschätzte Klassengrößen berechnet (Annahme: 25 Schüler pro Klasse). Zusätzlich wird der prozentuale Anteil der Schüler mit Förderbedarf je Kreis ermittelt.

# print(analyse_daten)
# A tibble: 13 × 9
#   kreis                 anzahl_schulen schueler_gesamt_kreis durchschnitt_schueler_pr…¹
#   <chr>                          <int>                 <dbl>                       <dbl>
# 1 Dithmarschen                       5                   908                      182. 
# 2 Hzgt. Lauenburg                    3                   953                      318. 
# 3 Kiel                               7                  1221                      174. 
# 4 Lübeck                            11                  2028                      184. 
# 5 Neumünster                         2                   366                      183  
# 6 Nordfriesland                      9                   696                       77.3
# 7 Ostholstein                        4                   968                      242  
# 8 Pinneberg                          6                  1153                      192. 
# 9 Plön                               3                   612                      204  
# 10 Rendsburg-Eckernförde              6                  1723                      287. 
# 11 Segeberg                           7                  1784                      255. 
# 12 Steinburg                          1                   215                      215  
# 13 Stormarn                           1                   374                      374  
# ℹ abbreviated name: ¹​durchschnitt_schueler_pro_schule
# ℹ 5 more variables: median_schueler_pro_schule <dbl>,
#   geschaetzte_klassen_pro_schule <dbl>, geschaetzte_klassengroesse <dbl>,
#   foerderbedarf_gesamt <dbl>, foerderanteil_prozent <dbl>
# print(paste("Anzahl Kreise in der Analyse:", nrow(analyse_daten)))
desc_stats <- analyse_daten %>%
  summarise(
    # Schülerzahlen
    mean_schueler_pro_schule = mean(durchschnitt_schueler_pro_schule, na.rm = TRUE),
    sd_schueler_pro_schule = sd(durchschnitt_schueler_pro_schule, na.rm = TRUE),
    min_schueler_pro_schule = min(durchschnitt_schueler_pro_schule, na.rm = TRUE),
    max_schueler_pro_schule = max(durchschnitt_schueler_pro_schule, na.rm = TRUE),

    # Klassengröße (geschätzt)
    mean_klassengroesse = mean(geschaetzte_klassengroesse, na.rm = TRUE),
    sd_klassengroesse = sd(geschaetzte_klassengroesse, na.rm = TRUE),
    min_klassengroesse = min(geschaetzte_klassengroesse, na.rm = TRUE),
    max_klassengroesse = max(geschaetzte_klassengroesse, na.rm = TRUE),

    # Förderanteil
    mean_foerderanteil = mean(foerderanteil_prozent, na.rm = TRUE),
    sd_foerderanteil = sd(foerderanteil_prozent, na.rm = TRUE),
    min_foerderanteil = min(foerderanteil_prozent, na.rm = TRUE),
    max_foerderanteil = max(foerderanteil_prozent, na.rm = TRUE)
  )

#DESKRIPTIVE STATISTIK

# print(desc_stats)
# A tibble: 1 × 13
#  mean_schueler_pro_schule sd_schueler_pro_schule min_schueler_pro_schule
#                     <dbl>                  <dbl>                   <dbl>
# 1                     222.                   74.7                    77.3
# ℹ 10 more variables: max_schueler_pro_schule <dbl>, mean_klassengroesse <dbl>,
#   sd_klassengroesse <dbl>, min_klassengroesse <dbl>, max_klassengroesse <dbl>,
#   mean_foerderanteil <dbl>, sd_foerderanteil <dbl>, min_foerderanteil <dbl>,
#   max_foerderanteil <dbl>, n_kreise <int>

Die Analyse umfasst 13 Kreise mit insgesamt 71 Schulen und 14.778 Schülern. Die durchschnittliche Schülerzahl pro Schule betrug 222 ± 74,7 Schüler, während die geschätzte Klassengröße bei 25,2 ± 0,861 Schülern lag. Der Anteil der Schüler mit Förderbedarf variierte stark zwischen 12,2% und 74,9%, mit einem Mittelwert von 30,3 ± 19,3%.

Das heißt: Obwohl die durchschnittlichen Klassengrößen mit 25,2 Schülern (± 0,9) sehr ähnlich sind, schwankt der Anteil der Schüler mit Förderbedarf zwischen nur 12,2% und fast 75%. Das zeigt, dass Klassenstärke allein nicht erklärt, warum manche Kreise viel mehr Förderbedarf ausweisen als andere.

Scatterplot: Klassengröße vs. Förderanteil

library(ggplot2)
library(ggrepel)

korrelation <- cor.test(analyse_daten$geschaetzte_klassengroesse,
                        analyse_daten$foerderanteil_prozent,
                        method = "pearson")

r_val <- as.numeric(korrelation$estimate)
p_val <- korrelation$p.value
p_text <- if (p_val < 0.001) "< 0.001" else format(round(p_val, 3), nsmall = 3)
label_rp <- paste0("Pearson r = ", round(r_val, 3), "\np = ", p_text)

x_pos <- max(analyse_daten$geschaetzte_klassengroesse, na.rm = TRUE) * 0.95
y_pos <- max(analyse_daten$foerderanteil_prozent, na.rm = TRUE) * 0.95

p_scatter_main <- ggplot(analyse_daten,
                         aes(x = geschaetzte_klassengroesse,
                             y = foerderanteil_prozent)) +
  geom_point(aes(size = schueler_gesamt_kreis), color = "steelblue", alpha = 0.75) +
  geom_smooth(method = "lm", se = TRUE, color = "red", alpha = 0.3) +
  geom_text_repel(aes(label = kreis), size = 3, max.overlaps = 20, 
                  box.padding = 0.5, point.padding = 0.3) +
  scale_size_area("Gesamtschüler\nim Kreis", max_size = 12, 
                  labels = function(x) format(x, big.mark = ".", decimal.mark = ",")) +
  annotate("label", x = x_pos, y = y_pos, label = label_rp, hjust = 1, vjust = 1,
           fill = "white", alpha = 0.9, size = 3.5, label.padding = unit(0.3, "lines")) +
  labs(title = "Zusammenhang zwischen Klassengröße und Förderbedarf",
       subtitle = paste0("Schleswig-Holstein, ", nrow(analyse_daten), " Kreise"),
       x = "Geschätzte durchschnittliche Klassengröße",
       y = "Anteil Schüler mit Förderbedarf (%)",
       caption = "Punktgröße = Gesamtschülerzahl im Kreis\nKlassengröße geschätzt basierend auf Schülerzahl pro Schule") +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 12),
    legend.position = "bottom"
  )

print(p_scatter_main)
## `geom_smooth()` using formula = 'y ~ x'

Der Scatterplot zeigt keine klare Trendlinie zwischen Klassengröße und Förderanteil.

4.3 HISTROGRAMME

p_hist_klassen <- ggplot(analyse_daten, aes(x = geschaetzte_klassengroesse)) +
  geom_histogram(binwidth = 2, color = "black", fill = "#4B9CD3", alpha = 0.8) +
  geom_vline(aes(xintercept = mean(geschaetzte_klassengroesse)), 
             color = "red", linetype = "dashed", linewidth = 1) +
  geom_vline(aes(xintercept = median(geschaetzte_klassengroesse)), 
             color = "darkgreen", linetype = "dotted", linewidth = 1) +
  annotate("text", x = mean(analyse_daten$geschaetzte_klassengroesse) + 1, 
           y = 2.5, label = paste0("μ = ", round(mean(analyse_daten$geschaetzte_klassengroesse), 1)), 
           color = "red", hjust = 0, size = 3.5) +
  annotate("text", x = median(analyse_daten$geschaetzte_klassengroesse) - 1, 
           y = 2, label = paste0("Median = ", round(median(analyse_daten$geschaetzte_klassengroesse), 1)), 
           color = "darkgreen", hjust = 1, size = 3.5) +
  labs(title = "Verteilung der geschätzten Klassengrößen",
       subtitle = "Basierend auf durchschnittlicher Schülerzahl pro Schule",
       x = "Geschätzte Klassengröße", y = "Anzahl Kreise") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold"))

print(p_hist_klassen)

p_hist_foerder <- ggplot(analyse_daten, aes(x = foerderanteil_prozent)) +
  geom_histogram(binwidth = 10, color = "black", fill = "#FFA756", alpha = 0.8) +
  geom_vline(aes(xintercept = mean(foerderanteil_prozent)), 
             color = "red", linetype = "dashed", linewidth = 1) +
  geom_vline(aes(xintercept = median(foerderanteil_prozent)), 
             color = "darkgreen", linetype = "dotted", linewidth = 1) +
  annotate("text", x = mean(analyse_daten$foerderanteil_prozent) + 5, 
           y = 2.5, label = paste0("μ = ", round(mean(analyse_daten$foerderanteil_prozent), 1), "%"), 
           color = "red", hjust = 0, size = 3.5) +
  annotate("text", x = median(analyse_daten$foerderanteil_prozent) - 5, 
           y = 2, label = paste0("Median = ", round(median(analyse_daten$foerderanteil_prozent), 1), "%"), 
           color = "darkgreen", hjust = 1, size = 3.5) +
  labs(title = "Verteilung des Förderanteils",
       subtitle = "Anteil Schüler mit Förderbedarf pro Kreis",
       x = "Anteil Förderbedarf (%)", y = "Anzahl Kreise") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold"))

print(p_hist_foerder)

Die beiden Histogramme zeigen, dass Klassengrößen eng beieinander liegen, während Förderanteile stark streuen.

4.4 Boxplot

p_boxplot <- analyse_daten %>%
  select(kreis, geschaetzte_klassengroesse, foerderanteil_prozent) %>%
  pivot_longer(cols = c(geschaetzte_klassengroesse, foerderanteil_prozent),
               names_to = "variable", values_to = "wert") %>%
  mutate(variable = case_when(
    variable == "geschaetzte_klassengroesse" ~ "Klassengröße",
    variable == "foerderanteil_prozent" ~ "Förderanteil (%)"
  )) %>%
  ggplot(aes(x = variable, y = wert)) +
  geom_boxplot(fill = "lightblue", alpha = 0.7) +
  geom_jitter(width = 0.2, alpha = 0.6, color = "darkblue") +
  facet_wrap(~variable, scales = "free_y") +
  labs(title = "Verteilungsübersicht der Hauptvariablen",
       x = "", y = "Wert") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold"))

print(p_boxplot)

Die Boxplots machen die homogene Verteilung der Klassengrößen und die heterogenen Förderanteile auf einen Blick sichtbar.

NORMALVERTEILUNG

# Shapiro-Wilk Test für geschätzte Klassengröße
shapiro_klasse <- shapiro.test(analyse_daten$geschaetzte_klassengroesse)
cat("Shapiro-Wilk Test für geschätzte Klassengröße:\n")
## Shapiro-Wilk Test für geschätzte Klassengröße:
print(shapiro_klasse)
## 
##  Shapiro-Wilk normality test
## 
## data:  analyse_daten$geschaetzte_klassengroesse
## W = 0.91846, p-value = 0.2391
# Interpretation
if(shapiro_klasse$p.value > 0.05) {
  cat("-> Die geschätzte Klassengröße ist vermutlich normalverteilt (p =", round(shapiro_klasse$p.value, 4), ")\n\n")
} else {
  cat("-> Die geschätzte Klassengröße ist nicht normalverteilt (p =", round(shapiro_klasse$p.value, 4), ")\n\n")
}
## -> Die geschätzte Klassengröße ist vermutlich normalverteilt (p = 0.2391 )
# Shapiro-Wilk Test für Förderanteil
shapiro_foerder <- shapiro.test(analyse_daten$foerderanteil_prozent)
cat("Shapiro-Wilk Test für Förderanteil:\n")
## Shapiro-Wilk Test für Förderanteil:
print(shapiro_foerder)
## 
##  Shapiro-Wilk normality test
## 
## data:  analyse_daten$foerderanteil_prozent
## W = 0.78112, p-value = 0.004102
# Interpretation
if(shapiro_foerder$p.value > 0.05) {
  cat("-> Der Förderanteil ist vermutlich normalverteilt (p =", round(shapiro_foerder$p.value, 4), ")\n")
} else {
  cat("-> Der Förderanteil ist nicht normalverteilt (p =", round(shapiro_foerder$p.value, 4), ")\n")
}
## -> Der Förderanteil ist nicht normalverteilt (p = 0.0041 )

Die geschätzten Klassengrößen folgen einer Normalverteilung (Shapiro-Wilk p = 0,239), sodass klassische, parametrische Verfahren wie die Pearson-Korrelation angemessen sind. Der Förderanteil ist dagegen nicht normalverteilt (p = 0,004), weshalb zusätzlich ein nicht-parametrischer Spearman-Test eingesetzt wurde. Hier also keine Normalverteilung.

KORRELATIONSANALYSE

# [1] "\n=== KORRELATIONSANALYSE ==="
# > # Hauptkorrelation: Klassengröße vs. Förderanteil
# print("1. Geschätzte Klassengröße vs. Förderanteil:")
# [1] "1. Geschätzte Klassengröße vs. Förderanteil:"
# > korr_hauptfrage <- cor.test(analyse_daten$geschaetzte_klassengroesse,
# +                             analyse_daten$foerderanteil_prozent,
# +                             method = "pearson")
# print(korr_hauptfrage)

#   Pearson's product-moment correlation

# data:  analyse_daten$geschaetzte_klassengroesse and analyse_daten$foerderanteil_prozent
# t = -1.2094, df = 11, p-value = 0.2519
# alternative hypothesis: true correlation is not equal to 0
# 95 percent confidence interval:
#  -0.7516785  0.2569009
# sample estimates:
#       cor 
# -0.3425755 
# > print("   Spearman (nicht-parametrisch):")
# [1] "   Spearman (nicht-parametrisch):"
# > korr_hauptfrage_spear <- cor.test(analyse_daten$geschaetzte_klassengroesse,
# +                                   analyse_daten$foerderanteil_prozent,
# +                                   method = "spearman")
# print(korr_hauptfrage_spear)

#   Spearman's rank correlation rho

# data:  analyse_daten$geschaetzte_klassengroesse and analyse_daten$foerderanteil_prozent
# S = 390, p-value = 0.8206
# alternative hypothesis: true rho is not equal to 0
# sample estimates:
#        rho 
-0.07142857 
## [1] -0.07142857
# > # Alternative: Schülerzahl vs. Förderanteil
# print("\n2. Durchschnittliche Schülerzahl vs. Förderanteil:")
# [1] "\n2. Durchschnittliche Schülerzahl vs. Förderanteil:"
# korr_schueler <- cor.test(analyse_daten$durchschnitt_schueler_pro_schule,
# +                           analyse_daten$foerderanteil_prozent,
# +                           method = "pearson")
# > print(korr_schueler)

#   Pearson's product-moment correlation

# data:  analyse_daten$durchschnitt_schueler_pro_schule and analyse_daten$foerderanteil_prozent
# t = 0.97595, df = 11, p-value = 0.3501
# alternative hypothesis: true correlation is not equal to 0
# 95 percent confidence interval:
#  -0.3181822  0.7211163
# sample estimates:
      cor 
## function (x, y = NULL, use = "everything", method = c("pearson", 
##     "kendall", "spearman")) 
## {
##     na.method <- pmatch(use, c("all.obs", "complete.obs", "pairwise.complete.obs", 
##         "everything", "na.or.complete"))
##     if (is.na(na.method)) 
##         stop("invalid 'use' argument")
##     method <- match.arg(method)
##     if (is.data.frame(y)) 
##         y <- as.matrix(y)
##     if (is.data.frame(x)) 
##         x <- as.matrix(x)
##     if (!is.matrix(x) && is.null(y)) 
##         stop("supply both 'x' and 'y' or a matrix-like 'x'")
##     if (!(is.numeric(x) || is.logical(x))) 
##         stop("'x' must be numeric")
##     stopifnot(is.atomic(x))
##     if (!is.null(y)) {
##         if (!(is.numeric(y) || is.logical(y))) 
##             stop("'y' must be numeric")
##         stopifnot(is.atomic(y))
##     }
##     Rank <- function(u) {
##         if (length(u) == 0L) 
##             u
##         else if (is.matrix(u)) {
##             if (nrow(u) > 1L) 
##                 apply(u, 2L, rank, na.last = "keep")
##             else row(u)
##         }
##         else rank(u, na.last = "keep")
##     }
##     if (method == "pearson") 
##         .Call(C_cor, x, y, na.method, FALSE)
##     else if (na.method %in% c(2L, 5L)) {
##         if (is.null(y)) {
##             .Call(C_cor, Rank(na.omit(x)), NULL, na.method, method == 
##                 "kendall")
##         }
##         else {
##             nas <- attr(na.omit(cbind(x, y)), "na.action")
##             dropNA <- function(x, nas) {
##                 if (length(nas)) {
##                   if (is.matrix(x)) 
##                     x[-nas, , drop = FALSE]
##                   else x[-nas]
##                 }
##                 else x
##             }
##             .Call(C_cor, Rank(dropNA(x, nas)), Rank(dropNA(y, 
##                 nas)), na.method, method == "kendall")
##         }
##     }
##     else if (na.method != 3L) {
##         x <- Rank(x)
##         if (!is.null(y)) 
##             y <- Rank(y)
##         .Call(C_cor, x, y, na.method, method == "kendall")
##     }
##     else {
##         if (is.null(y)) {
##             ncy <- ncx <- ncol(x)
##             if (ncx == 0) 
##                 stop("'x' is empty")
##             r <- matrix(0, nrow = ncx, ncol = ncy)
##             for (i in seq_len(ncx)) {
##                 for (j in seq_len(i)) {
##                   x2 <- x[, i]
##                   y2 <- x[, j]
##                   ok <- complete.cases(x2, y2)
##                   x2 <- rank(x2[ok])
##                   y2 <- rank(y2[ok])
##                   r[i, j] <- if (any(ok)) 
##                     .Call(C_cor, x2, y2, 1L, method == "kendall")
##                   else NA
##                 }
##             }
##             r <- r + t(r) - diag(diag(r))
##             rownames(r) <- colnames(x)
##             colnames(r) <- colnames(x)
##             r
##         }
##         else {
##             if (length(x) == 0L || length(y) == 0L) 
##                 stop("both 'x' and 'y' must be non-empty")
##             matrix_result <- is.matrix(x) || is.matrix(y)
##             if (!is.matrix(x)) 
##                 x <- matrix(x, ncol = 1L)
##             if (!is.matrix(y)) 
##                 y <- matrix(y, ncol = 1L)
##             ncx <- ncol(x)
##             ncy <- ncol(y)
##             r <- matrix(0, nrow = ncx, ncol = ncy)
##             for (i in seq_len(ncx)) {
##                 for (j in seq_len(ncy)) {
##                   x2 <- x[, i]
##                   y2 <- y[, j]
##                   ok <- complete.cases(x2, y2)
##                   x2 <- rank(x2[ok])
##                   y2 <- rank(y2[ok])
##                   r[i, j] <- if (any(ok)) 
##                     .Call(C_cor, x2, y2, 1L, method == "kendall")
##                   else NA
##                 }
##             }
##             rownames(r) <- colnames(x)
##             colnames(r) <- colnames(y)
##             if (matrix_result) 
##                 r
##             else drop(r)
##         }
##     }
## }
## <bytecode: 0x13695eff8>
## <environment: namespace:stats>
0.2822929 
## [1] 0.2822929

Die Pearson-Korrelation zwischen Klassengröße und Förderanteil betrug r = –0,343 (p = 0,252), die Spearman-Rangkorrelation ρ = –0,071 (p = 0,821); beide Zusammenhänge sind statistisch nicht signifikant.Ein negativer r-Wert bedeutet zwar einen leichten Trend zu weniger Förderbedarf bei größeren Klassen, dieser Effekt ist aber statistisch nicht belastbar.

#REGRESSIONSANALYSE

cat("\n=== REGRESSIONSANALYSE ===\n")
## 
## === REGRESSIONSANALYSE ===
cat("1. HAUPTMODELL: Klassengröße -> Förderanteil\n")
## 1. HAUPTMODELL: Klassengröße -> Förderanteil
# Lineares Modell erstellen
hauptmodell <- lm(foerderanteil_prozent ~ geschaetzte_klassengroesse, data = analyse_daten)

# Zusammenfassung des Modells
summary_haupt <- summary(hauptmodell)

# Ergebnis ausgeben
print(summary_haupt)
## 
## Call:
## lm(formula = foerderanteil_prozent ~ geschaetzte_klassengroesse, 
##     data = analyse_daten)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -25.883 -10.656  -5.692   7.036  34.420 
## 
## Coefficients:
##                            Estimate Std. Error t value Pr(>|t|)
## (Intercept)                 224.014    160.233   1.398    0.190
## geschaetzte_klassengroesse   -7.683      6.353  -1.209    0.252
## 
## Residual standard error: 18.96 on 11 degrees of freedom
## Multiple R-squared:  0.1174, Adjusted R-squared:  0.03712 
## F-statistic: 1.463 on 1 and 11 DF,  p-value: 0.2519
# Ergebnis (kommentiert als Referenz)
# Call:
# lm(formula = foerderanteil_prozent ~ geschaetzte_klassengroesse, data = analyse_daten)
#
# Residuals:
#     Min      1Q  Median      3Q     Max 
# -25.883 -10.656  -5.692   7.036  34.420 
#
# Coefficients:
#                           Estimate Std. Error t value Pr(>|t|)
# (Intercept)                 224.014    160.233   1.398    0.190
# geschaetzte_klassengroesse   -7.683      6.353  -1.209    0.252
#
# Residual standard error: 18.96 on 11 degrees of freedom
# Multiple R-squared:  0.1174, Adjusted R-squared:  0.03712 
# F-statistic: 1.463 on 1 and 11 DF, p-value: 0.2519

Das Regressionsmodell erklärt nur 11,7% der Variation im Förderanteil (R² = 0,1174). Der geschätzte Einfluss der Klassengröße (β = –7,683; p = 0,252) ist nicht signifikant. Das heißt, selbst wenn ich eine lineare Funktion anlege, trägt die Klassengröße kaum zur Vorhersage des Förderanteils bei.

SCHÜLERZAHL VS. FÖRDERANTEIL

cat("\n2. ALTERNATIVMODELL: Schülerzahl -> Förderanteil\n")
## 
## 2. ALTERNATIVMODELL: Schülerzahl -> Förderanteil
alt_modell <- lm(foerderanteil_prozent ~ durchschnitt_schueler_pro_schule, data = analyse_daten)
summary_alt <- summary(alt_modell)
print(summary_alt)
## 
## Call:
## lm(formula = foerderanteil_prozent ~ durchschnitt_schueler_pro_schule, 
##     data = analyse_daten)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -21.735 -13.911   0.490   4.339  45.067 
## 
## Coefficients:
##                                  Estimate Std. Error t value Pr(>|t|)
## (Intercept)                      14.12457   17.45779   0.809    0.436
## durchschnitt_schueler_pro_schule  0.07299    0.07479   0.976    0.350
## 
## Residual standard error: 19.36 on 11 degrees of freedom
## Multiple R-squared:  0.07969,    Adjusted R-squared:  -0.003975 
## F-statistic: 0.9525 on 1 and 11 DF,  p-value: 0.3501
# Ergebnis als Kommentar:
# Call:
# lm(formula = foerderanteil_prozent ~ durchschnitt_schueler_pro_schule, data = analyse_daten)
#
# Residuals:
#    Min      1Q  Median      3Q     Max 
# -21.735 -13.911   0.490   4.339  45.067 
#
# Coefficients:
#                                 Estimate Std. Error t value Pr(>|t|)
# (Intercept)                      14.12457   17.45779   0.809    0.436
# durchschnitt_schueler_pro_schule  0.07299    0.07479   0.976    0.350
#
# Residual standard error: 19.36 on 11 degrees of freedom
# Multiple R-squared:  0.07969, Adjusted R-squared:  -0.003975 
# F-statistic: 0.9525 on 1 and 11 DF,  p-value: 0.3501

5. FAZIT

Es gibt keine Belege dafür, dass größere oder kleinere Klassen systematisch mehr oder weniger Förderbedarf haben. Schulpolitische Entscheidungen zu Förderressourcen sollten sich nicht allein an der Klassengröße orientieren. Vielmehr müssen andere Faktoren (z. B. regionale Sozialstruktur, spezifische Inklusionspolitik, Förderangebote vor Ort) untersucht werden, um die Ursachen für unterschiedliche Förderbedarfe zu verstehen.

Es besteht also kein signifikanter Zusammenhang zwischen Klassengröße und Förderanteil. Die Nullhypothese H0 wird beibehalten.

LIMITATIONEN