From ff33a5a2960fba3e69be0ac0b1e42605bd0f36e1 Mon Sep 17 00:00:00 2001 From: Jochen Hanisch-Johannsen Date: Wed, 3 Sep 2025 00:00:36 +0200 Subject: [PATCH] first commit --- Thermometer.csv | 69 +++ config_visible_learning.py | 38 ++ export/cluster_profile.json | 84 +++ export/clusterzuordnung.csv | 69 +++ export/data_quality_report.json | 9 + export/deskriptiv.csv | 5 + export/signifikanz_ranking.csv | 69 +++ export/tests_summary.json | 13 + export/werte_all.json | 922 ++++++++++++++++++++++++++++ visible-learning.py | 1001 +++++++++++++++++++++++++++++++ 10 files changed, 2279 insertions(+) create mode 100644 Thermometer.csv create mode 100644 config_visible_learning.py create mode 100644 export/cluster_profile.json create mode 100644 export/clusterzuordnung.csv create mode 100644 export/data_quality_report.json create mode 100644 export/deskriptiv.csv create mode 100644 export/signifikanz_ranking.csv create mode 100644 export/tests_summary.json create mode 100644 export/werte_all.json create mode 100644 visible-learning.py diff --git a/Thermometer.csv b/Thermometer.csv new file mode 100644 index 0000000..caf731a --- /dev/null +++ b/Thermometer.csv @@ -0,0 +1,69 @@ +Thermometer_ID,Stichwort,Effektstärke +5.01,Vorausgehende Fähigkeiten & Intelligenz,0.96 +5.02,Vorausgehendes Leistungsniveau,0.73 +5.03,Beziehung zwischen Schul- und Berufsleistungen,0.37 +5.04,Beziehung zwischen Schul- und Universitätsleistungen,0.55 +5.05,Erkenntnisstufen,1.28 +5.06,Exekutive Funktionen,0.62 +5.07,Stärke des Arbeitsgedächtnisses,0.63 +5.08,Vorschulische nicht-kognitive Fähigkeiten,0.20 +5.09,Gekreuzte Lateralität,-0.03 +5.10,Feldunabhängigkeit,0.94 +5.11,Beurteilung der eigenen Leistungsfähigkeit,0.96 +5.12,Kreativität und Lernleistung in Beziehung setzen,0.40 +5.13,Kritisches Denken,0.84 +5.14,Beharrlichkeit und Zuversicht (Mindset),0.19 +5.15,Beharrlichkeit und Zuversicht (Achtsamkeit),0.26 +5.16,Beharrlichkeit und Zuversicht (Durchhaltevermögen),0.35 +5.17,Beharrlichkeit und Zuversicht (Konzentration / Ausdauer und Engagement),0.41 +5.18,Beharrlichkeit und Zuversicht (Selbstwirksamkeitserwartung),0.64 +5.19,Beharrlichkeit und Zuversicht (Positives Selbstbild),0.51 +5.20,Beharrlichkeit und Zuversicht (Selbstkontrolle),0.66 +5.21,Schülerpersönlichkeit,0.18 +5.22,Perfektionismus,-0.03 +5.23,Emotionen,0.61 +5.24,Emotionen (Emotionale Intelligenz),0.50 +5.25,Emotionen (Wohlbefinden),0.08 +5.26,Positiv-aktivierend (Freude),0.50 +5.27,Positiv-aktivierend (Hoffnung),0.20 +5.28,Positiv-aktivierend (Neugierde),0.74 +5.29,Positiv-aktivierend (Glücklichsein),0.54 +5.30,Positiv-aktivierend (Entspannung),0.16 +5.31,negativ-aktivierend (Angst),-0.40 +5.32,negativ-aktivierend (Depressionen),-0.30 +5.33,negativ-aktivierend (Wut),-0.65 +5.34,negativ-aktivierend (Frustration),-0.04 +5.35,negativ-aktivierend (Aggression und Gewalt),0.03 +5.36,negativ-aktivierend (Langeweile),-0.46 +5.37,kognitive Dispositionen (Morgentypus vs. Abendtypus),0.18 +5.38,kognitive Dispositionen (Prokrastination),-0.41 +6.01,Sozioökonomischer Status,0.56 +6.02,Bezug staatlicher Transferleistungen,-0.12 +6.03,Erwerbstätigkeit der Mutter,0.05 +6.04,Stipendien,0.03 +6.05,Einwanderungsstatus,0.05 +6.06,Familienstruktur,0.14 +6.07,Geschieden,-0.26 +6.08,nicht-geschieden vs. wiederverheiratet,0.24 +6.09,Adoption,0.21 +6.10,Kinderheime,0.33 +6.11,Häusliches Anregungsniveau,0.40 +6.12,Elternunterstützung beim Lernen,0.41 +6.13,Elterliche Autonomieunterstützung (Familienhilfe),0.06 +6.14,Elternerwartungen,0.49 +6.15,Körperliche Züchtigung,-0.33 +6.16,Väter,0.21 +6.17,Schulwechsel,-0.38 +6.18,Fernsehen,-0.15 +6.19,Programme für Eltern,0.39 +6.20,Hausbesuche durch Lehrpersonen,0.22 +7.01,Finanzielle Ausstattung,0.19 +7.02,Accountability / Rechenschaftspflicht,0.27 +7.03,Leistungsbezogene Bezahlung,0.05 +7.04,Qualität des Schulgebäudes,0.24 +7.05,Vertragsschulen / Charter-Schulen,0.04 +7.06,Konfessionsschulen,0.23 +7.07,Monoedukation,0.07 +7.08,Dauer der Sommerferien,-0.09 +7.09,Sommerschulen,0.17 +7.10,Schulkalender / Stundenplan,0.10 diff --git a/config_visible_learning.py b/config_visible_learning.py new file mode 100644 index 0000000..5c6f36a --- /dev/null +++ b/config_visible_learning.py @@ -0,0 +1,38 @@ +""" +Konfiguration Visible Learning + +Diese Datei steuert die Analysen der Effektstärken aus Hattie (Visible Learning). + +- csv_file: Pfad zur Eingabedatei (eine CSV, die alle Kapitel enthalten kann). +- k_clusters: Anzahl der Cluster für K-Means. + +- export_fig_visual: True = HTML-Export der Plots. +- export_fig_png: True = PNG-Export der Plots (setzt Kaleido voraus). + +- theme: Darstellungs-Theme ("dark" oder "light"). + +Kapitelsteuerung: +- selected_kapitel: Nummer eines Kapitels (z. B. 5), das isoliert betrachtet werden soll. + None = kein Filter, d. h. gesamte CSV in einem Schwung analysieren. +- analyse_all: True = alle Kapitel sequenziell einzeln durchlaufen und auswerten. + False = nur den Filter aus selected_kapitel beachten. +""" +# config_visible-learning.py + +# Pfad zur Eingabedatei +csv_file = "Thermometer.csv" + +# Anzahl der Cluster für K-Means +k_clusters = 4 + +# Exportoptionen +export_fig_visual = False # HTML-Export +export_fig_png = False # PNG-Export + +# Plot-Theme (dark / light) +theme = "dark" + +# Kapitelsteuerung +selected_kapitel = None # Nummer des Kapitels (z.B. 5), None = kein Filter +analyse_all = False # True = alle Kapitel durchlaufen +export_werte_all = True # Wertedatei (werte_all.json) exportieren diff --git a/export/cluster_profile.json b/export/cluster_profile.json new file mode 100644 index 0000000..67437df --- /dev/null +++ b/export/cluster_profile.json @@ -0,0 +1,84 @@ +{ + "schema": { + "fields": [ + { + "name": "Cluster", + "type": "integer" + }, + { + "name": "n", + "type": "integer" + }, + { + "name": "Ø d", + "type": "number" + }, + { + "name": "Kapitelverteilung", + "type": "string" + }, + { + "name": "Top_Terme", + "type": "string" + } + ], + "primaryKey": [ + "Cluster" + ], + "pandas_version": "1.4.0" + }, + "data": [ + { + "Cluster": 0, + "n": 21, + "Ø d": 0.654, + "Kapitelverteilung": { + "5": 21 + }, + "Top_Terme": [ + "und", + "zuversicht", + "beharrlichkeit" + ] + }, + { + "Cluster": 1, + "n": 20, + "Ø d": 0.128, + "Kapitelverteilung": { + "6": 20 + }, + "Top_Terme": [ + "geschieden", + "kinderheime", + "stipendien" + ] + }, + { + "Cluster": 2, + "n": 17, + "Ø d": -0.049, + "Kapitelverteilung": { + "5": 17 + }, + "Top_Terme": [ + "aktivierend", + "negativ", + "kognitive" + ] + }, + { + "Cluster": 3, + "n": 10, + "Ø d": 0.127, + "Kapitelverteilung": { + "7": 10 + }, + "Top_Terme": [ + "konfessionsschulen", + "sommerschulen", + "monoedukation" + ] + } + ] +} \ No newline at end of file diff --git a/export/clusterzuordnung.csv b/export/clusterzuordnung.csv new file mode 100644 index 0000000..14f5018 --- /dev/null +++ b/export/clusterzuordnung.csv @@ -0,0 +1,69 @@ +Thermometer_ID,Stichwort,Effektstärke,Kapitel,Kapitelname,Bin,Silhouette_point,Outlier_IQR,Text_Dimension,Cluster +5.01,Vorausgehende Fähigkeiten & Intelligenz,0.96,5,Lernende,hoch,0.6502913752913749,False,-0.08735409337133096,0 +5.02,Vorausgehendes Leistungsniveau,0.73,5,Lernende,hoch,0.7260754716981136,False,-0.07336449853523334,0 +5.03,Beziehung zwischen Schul- und Berufsleistungen,0.37,5,Lernende,gering,0.2835203366058893,False,-0.17512188415193075,0 +5.04,Beziehung zwischen Schul- und Universitätsleistungen,0.55,5,Lernende,mittel,0.680520117762513,False,-0.175121884151931,0 +5.05,Erkenntnisstufen,1.28,5,Lernende,hoch,0.5057964601769913,True,-0.05914975460796303,0 +5.06,Exekutive Funktionen,0.62,5,Lernende,mittel,0.7281195079086115,False,-0.05914975460796313,0 +5.07,Stärke des Arbeitsgedächtnisses,0.63,5,Lernende,mittel,0.7313852813852812,False,-0.07892435655637323,0 +5.08,Vorschulische nicht-kognitive Fähigkeiten,0.2,5,Lernende,gering,0.4001572327044027,False,-0.09398809922530565,2 +5.09,Gekreuzte Lateralität,-0.03,5,Lernende,negativ,0.6501826722338218,False,-0.07336449853523311,2 +5.1,Feldunabhängigkeit,0.94,5,Lernende,hoch,0.6583828775267535,False,-0.05914975460796317,0 +5.11,Beurteilung der eigenen Leistungsfähigkeit,0.96,5,Lernende,hoch,0.6502913752913749,False,-0.08382750975171666,0 +5.12,Kreativität und Lernleistung in Beziehung setzen,0.4,5,Lernende,mittel,0.3880890052355992,False,-0.14757153731358338,0 +5.13,Kritisches Denken,0.84,5,Lernende,hoch,0.6930555555555555,False,-0.07336449853523312,0 +5.14,Beharrlichkeit und Zuversicht (Mindset),0.19,5,Lernende,gering,0.42788461538461536,False,-0.33543250302056493,2 +5.15,Beharrlichkeit und Zuversicht (Achtsamkeit),0.26,5,Lernende,gering,0.16621376811594119,False,-0.33543250302056515,2 +5.16,Beharrlichkeit und Zuversicht (Durchhaltevermögen),0.35,5,Lernende,gering,0.200073637702503,False,-0.33543250302056504,0 +5.17,Beharrlichkeit und Zuversicht (Konzentration / Ausdauer und Engagement),0.41,5,Lernende,mittel,0.4177336747759273,False,-0.3218185254749976,0 +5.18,Beharrlichkeit und Zuversicht (Selbstwirksamkeitserwartung),0.64,5,Lernende,mittel,0.7331058020477826,False,-0.3354325030205649,0 +5.19,Beharrlichkeit und Zuversicht (Positives Selbstbild),0.51,5,Lernende,mittel,0.6344374342797038,False,-0.3021008406853567,0 +5.2,Beharrlichkeit und Zuversicht (Selbstkontrolle),0.66,5,Lernende,mittel,0.7335820895522391,False,-0.3354325030205649,0 +5.21,Schülerpersönlichkeit,0.18,5,Lernende,gering,0.45180722891566244,False,-0.07336449853523312,2 +5.22,Perfektionismus,-0.03,5,Lernende,negativ,0.6501826722338218,False,-0.07336449853523314,2 +5.23,Emotionen,0.61,5,Lernende,mittel,0.7232381801962534,False,-0.1013942427833901,0 +5.24,Emotionen (Emotionale Intelligenz),0.5,5,Lernende,mittel,0.6195931477516049,False,-0.1010290138898771,0 +5.25,Emotionen (Wohlbefinden),0.08,5,Lernende,gering,0.5842661691542289,False,-0.09745012386374446,2 +5.26,Positiv-aktivierend (Freude),0.5,5,Lernende,mittel,0.6195931477516049,False,0.7827951695376463,0 +5.27,Positiv-aktivierend (Hoffnung),0.2,5,Lernende,gering,0.4001572327044027,False,0.7827951695376463,2 +5.28,Positiv-aktivierend (Neugierde),0.74,5,Lernende,hoch,0.7238450074515644,False,0.5855547077500068,0 +5.29,Positiv-aktivierend (Glücklichsein),0.54,5,Lernende,mittel,0.6708582834331347,False,0.7827951695376463,0 +5.3,Positiv-aktivierend (Entspannung),0.16,5,Lernende,gering,0.48663294797687695,False,0.7827951695376463,2 +5.31,negativ-aktivierend (Angst),-0.4,5,Lernende,negativ,0.608739837398373,False,0.4425504899470148,2 +5.32,negativ-aktivierend (Depressionen),-0.3,5,Lernende,negativ,0.6266841317365268,False,0.4425504899470148,2 +5.33,negativ-aktivierend (Wut),-0.65,5,Lernende,negativ,0.510747535596933,True,0.44255048994701485,2 +5.34,negativ-aktivierend (Frustration),-0.04,5,Lernende,negativ,0.6507201646090555,False,0.6056986180050421,2 +5.35,negativ-aktivierend (Aggression und Gewalt),0.03,5,Lernende,gering,0.6225686498855841,False,0.30890529874855743,2 +5.36,negativ-aktivierend (Langeweile),-0.46,5,Lernende,negativ,0.5871794871794872,False,0.4425504899470148,2 +5.37,kognitive Dispositionen (Morgentypus vs. Abendtypus),0.18,5,Lernende,gering,0.45180722891566244,False,-0.09227886985139404,2 +5.38,kognitive Dispositionen (Prokrastination),-0.41,5,Lernende,negativ,0.6059563758389256,False,-0.08917102326885956,2 +6.01,Sozioökonomischer Status,0.56,6,Elternhaus und Familie,mittel,0.6827943498774371,False,-0.07336449853523314,1 +6.02,Bezug staatlicher Transferleistungen,-0.12,6,Elternhaus und Familie,negativ,0.772215227319828,False,-0.07336449853523314,1 +6.03,Erwerbstätigkeit der Mutter,0.05,6,Elternhaus und Familie,gering,0.8306537734304565,False,-0.08585665050056006,1 +6.04,Stipendien,0.03,6,Elternhaus und Familie,gering,0.82487518707422,False,-0.07336449853523311,1 +6.05,Einwanderungsstatus,0.05,6,Elternhaus und Familie,gering,0.8306537734304565,False,-0.07336449853523312,1 +6.06,Familienstruktur,0.14,6,Elternhaus und Familie,gering,0.8378335891817927,False,-0.059149754607963165,1 +6.07,Geschieden,-0.26,6,Elternhaus und Familie,negativ,0.7061030196788244,False,-0.08431062057732165,1 +6.08,nicht-geschieden vs. wiederverheiratet,0.24,6,Elternhaus und Familie,gering,0.8324161165454884,False,-0.09341002740120666,1 +6.09,Adoption,0.21,6,Elternhaus und Familie,gering,0.8381014976799916,False,-0.0733644985352332,1 +6.1,Kinderheime,0.33,6,Elternhaus und Familie,gering,0.8071221370551951,False,-0.07336449853523312,1 +6.11,Häusliches Anregungsniveau,0.4,6,Elternhaus und Familie,mittel,0.7824192735239991,False,-0.07336449853523314,1 +6.12,Elternunterstützung beim Lernen,0.41,6,Elternhaus und Familie,mittel,0.77761147335638,False,-0.07336449853523314,1 +6.13,Elterliche Autonomieunterstützung (Familienhilfe),0.06,6,Elternhaus und Familie,gering,0.8320762909326382,False,-0.07336449853523312,1 +6.14,Elternerwartungen,0.49,6,Elternhaus und Familie,mittel,0.7301343867848403,False,-0.07336449853523314,1 +6.15,Körperliche Züchtigung,-0.33,6,Elternhaus und Familie,negativ,0.668243380062615,False,-0.07336449853523312,1 +6.16,Väter,0.21,6,Elternhaus und Familie,gering,0.8381014976799916,False,-0.07336449853523312,1 +6.17,Schulwechsel,-0.38,6,Elternhaus und Familie,negativ,0.6384645645869302,False,-0.07336449853523312,1 +6.18,Fernsehen,-0.15,6,Elternhaus und Familie,negativ,0.7598477443369885,False,-0.059149754607963165,1 +6.19,Programme für Eltern,0.39,6,Elternhaus und Familie,gering,0.7865197915104075,False,-0.07336449853523314,1 +6.2,Hausbesuche durch Lehrpersonen,0.22,6,Elternhaus und Familie,gering,0.8366902739695423,False,-0.07336449853523314,1 +7.01,Finanzielle Ausstattung,0.19,7,Schule und Gesellschaft,gering,0.9251410228772312,False,-0.07336449853523312,3 +7.02,Accountability / Rechenschaftspflicht,0.27,7,Schule und Gesellschaft,gering,0.8900556973256574,False,-0.0733644985352329,3 +7.03,Leistungsbezogene Bezahlung,0.05,7,Schule und Gesellschaft,gering,0.9174717855887603,False,-0.07336449853523314,3 +7.04,Qualität des Schulgebäudes,0.24,7,Schule und Gesellschaft,gering,0.9083468761774898,False,-0.07892435655637318,3 +7.05,Vertragsschulen / Charter-Schulen,0.04,7,Schule und Gesellschaft,gering,0.9128781474571108,False,-0.07336449853523315,3 +7.06,Konfessionsschulen,0.23,7,Schule und Gesellschaft,gering,0.9129250266162217,False,-0.07336449853523314,3 +7.07,Monoedukation,0.07,7,Schule und Gesellschaft,gering,0.9235929535014257,False,-0.07336449853523312,3 +7.08,Dauer der Sommerferien,-0.09,7,Schule und Gesellschaft,negativ,0.8327092370446383,False,-0.08477035593782843,3 +7.09,Sommerschulen,0.17,7,Schule und Gesellschaft,gering,0.9281937118300768,False,-0.07336449853523315,3 +7.1,Schulkalender / Stundenplan,0.1,7,Schule und Gesellschaft,gering,0.9281794453692277,False,-0.07336449853523314,3 diff --git a/export/data_quality_report.json b/export/data_quality_report.json new file mode 100644 index 0000000..82a8e40 --- /dev/null +++ b/export/data_quality_report.json @@ -0,0 +1,9 @@ +{ + "n_rows": 68, + "duplicate_ids": {}, + "n_duplicates": 0, + "invalid_kapitel_entries": [], + "effekt_min": -0.65, + "effekt_max": 1.28, + "empty_stichwort": 0 +} \ No newline at end of file diff --git a/export/deskriptiv.csv b/export/deskriptiv.csv new file mode 100644 index 0000000..e3c776f --- /dev/null +++ b/export/deskriptiv.csv @@ -0,0 +1,5 @@ +,n,mean,std,min,q1,median,q3,max,skew,kurtosis +Gesamt,68,0.2458823529411765,0.37290403428819446,-0.65,0.0475,0.21,0.5,1.28,0.12925197443518738,0.2800914374662091 +Kapitel 5,38,0.3394736842105263,0.43649059535035,-0.65,0.1,0.385,0.6275,1.28,-0.29469572711835434,-0.11719052918009742 +Kapitel 6,20,0.1275,0.27132957236929645,-0.38,-0.0075,0.175,0.34500000000000003,0.56,-0.3580457406718272,-0.6941765728667146 +Kapitel 7,10,0.127,0.11264990013311152,-0.09,0.05500000000000001,0.135,0.22,0.27,-0.5585413007473659,-0.22749527826960758 diff --git a/export/signifikanz_ranking.csv b/export/signifikanz_ranking.csv new file mode 100644 index 0000000..72e0ba3 --- /dev/null +++ b/export/signifikanz_ranking.csv @@ -0,0 +1,69 @@ +Thermometer_ID,Stichwort,Effektstärke,Kapitel,Kapitelname,Bin,Silhouette_point,Outlier_IQR,Text_Dimension,abs_d,SignifikanzScore,Rank_abs,Rank_score,Impact_Label +5.05,Erkenntnisstufen,1.28,5,Lernende,hoch,0.5057964601769913,True,-0.05914975460796303,1.28,0.7782633229981433,1,1,hoch+ +5.01,Vorausgehende Fähigkeiten & Intelligenz,0.96,5,Lernende,hoch,0.6502913752913749,False,-0.08735409337133096,0.96,0.7005156686176717,2,2,hoch+ +5.11,Beurteilung der eigenen Leistungsfähigkeit,0.96,5,Lernende,hoch,0.6502913752913749,False,-0.08382750975171666,0.96,0.7005156686176717,2,2,hoch+ +5.1,Feldunabhängigkeit,0.94,5,Lernende,hoch,0.6583828775267535,False,-0.05914975460796317,0.94,0.6951632881526102,4,4,hoch+ +5.13,Kritisches Denken,0.84,5,Lernende,hoch,0.6930555555555555,False,-0.07336449853523312,0.84,0.6653646480780675,5,5,hoch+ +5.28,Positiv-aktivierend (Neugierde),0.74,5,Lernende,hoch,0.7238450074515644,False,0.5855547077500068,0.74,0.6335275154343559,6,6,hoch+ +5.02,Vorausgehendes Leistungsniveau,0.73,5,Lernende,hoch,0.7260754716981136,False,-0.07336449853523334,0.73,0.6298983936260716,7,7,hoch+ +5.2,Beharrlichkeit und Zuversicht (Selbstkontrolle),0.66,5,Lernende,mittel,0.7335820895522391,False,-0.3354325030205649,0.66,0.6002389791577778,8,8,mittel+ +5.18,Beharrlichkeit und Zuversicht (Selbstwirksamkeitserwartung),0.64,5,Lernende,mittel,0.7331058020477826,False,-0.3354325030205649,0.64,0.5903889528895614,10,9,mittel+ +5.07,Stärke des Arbeitsgedächtnisses,0.63,5,Lernende,mittel,0.7313852813852812,False,-0.07892435655637323,0.63,0.5846857686644674,11,10,mittel+ +5.06,Exekutive Funktionen,0.62,5,Lernende,mittel,0.7281195079086115,False,-0.05914975460796313,0.62,0.5781714067558055,12,11,mittel+ +5.23,Emotionen,0.61,5,Lernende,mittel,0.7232381801962534,False,-0.1013942427833901,0.61,0.570808962558881,13,12,mittel+ +6.01,Sozioökonomischer Status,0.56,6,Elternhaus und Familie,mittel,0.6827943498774371,False,-0.07336449853523314,0.56,0.5255780466260127,14,13,mittel+ +5.04,Beziehung zwischen Schul- und Universitätsleistungen,0.55,5,Lernende,mittel,0.680520117762513,False,-0.175121884151931,0.55,0.5195841925705693,15,14,mittel+ +6.14,Elternerwartungen,0.49,6,Elternhaus und Familie,mittel,0.7301343867848403,False,-0.07336449853523314,0.49,0.5168291137953939,20,15,mittel+ +5.29,Positiv-aktivierend (Glücklichsein),0.54,5,Lernende,mittel,0.6708582834331347,False,0.7827951695376463,0.54,0.5097122300292544,16,16,mittel+ +6.12,Elternunterstützung beim Lernen,0.41,6,Elternhaus und Familie,mittel,0.77761147335638,False,-0.07336449853523314,0.41,0.5033521249392942,22,17,mittel+ +6.11,Häusliches Anregungsniveau,0.4,6,Elternhaus und Familie,mittel,0.7824192735239991,False,-0.07336449853523314,0.4,0.5010759709839467,25,18,mittel+ +6.19,Programme für Eltern,0.39,6,Elternhaus und Familie,gering,0.7865197915104075,False,-0.07336449853523314,0.39,0.4984285305205778,28,19,gering+ +7.02,Accountability / Rechenschaftspflicht,0.27,7,Schule und Gesellschaft,gering,0.8900556973256574,False,-0.0733644985352329,0.27,0.49517951792876724,35,20,gering+ +7.04,Qualität des Schulgebäudes,0.24,7,Schule und Gesellschaft,gering,0.9083468761774898,False,-0.07892435655637318,0.24,0.4903814393455832,38,21,gering+ +7.06,Konfessionsschulen,0.23,7,Schule und Gesellschaft,gering,0.9129250266162217,False,-0.07336449853523314,0.23,0.4879847311783926,40,22,gering+ +6.1,Kinderheime,0.33,6,Elternhaus und Familie,gering,0.8071221370551951,False,-0.07336449853523312,0.33,0.4804436947330976,32,23,gering+ +5.33,negativ-aktivierend (Wut),-0.65,5,Lernende,negativ,0.510747535596933,True,0.44255048994701485,0.65,-0.47846238112862827,9,24,mittel− +5.19,Beharrlichkeit und Zuversicht (Positives Selbstbild),0.51,5,Lernende,mittel,0.6344374342797038,False,-0.3021008406853567,0.51,0.476193170818376,17,25,mittel+ +7.01,Finanzielle Ausstattung,0.19,7,Schule und Gesellschaft,gering,0.9251410228772312,False,-0.07336449853523312,0.19,0.4751974964285984,46,26,gering+ +7.09,Sommerschulen,0.17,7,Schule und Gesellschaft,gering,0.9281937118300768,False,-0.07336449853523315,0.17,0.46720000000000006,50,27,gering+ +5.24,Emotionen (Emotionale Intelligenz),0.5,5,Lernende,mittel,0.6195931477516049,False,-0.1010290138898771,0.5,0.4636006893230006,18,28,mittel+ +5.26,Positiv-aktivierend (Freude),0.5,5,Lernende,mittel,0.6195931477516049,False,0.7827951695376463,0.5,0.4636006893230006,18,28,mittel+ +6.08,nicht-geschieden vs. wiederverheiratet,0.24,6,Elternhaus und Familie,gering,0.8324161165454884,False,-0.09341002740120666,0.24,0.4505217237410541,38,30,gering+ +6.2,Hausbesuche durch Lehrpersonen,0.22,6,Elternhaus und Familie,gering,0.8366902739695423,False,-0.07336449853523314,0.22,0.44316543498795136,41,31,gering+ +6.09,Adoption,0.21,6,Elternhaus und Familie,gering,0.8381014976799916,False,-0.0733644985352332,0.21,0.4391062543347549,42,32,gering+ +6.16,Väter,0.21,6,Elternhaus und Familie,gering,0.8381014976799916,False,-0.07336449853523312,0.21,0.4391062543347549,42,32,gering+ +7.1,Schulkalender / Stundenplan,0.1,7,Schule und Gesellschaft,gering,0.9281794453692277,False,-0.07336449853523314,0.1,0.4335925108470548,55,34,gering+ +5.36,negativ-aktivierend (Langeweile),-0.46,5,Lernende,negativ,0.5871794871794872,False,0.4425504899470148,0.46,-0.42738519654552776,21,35,mittel− +7.07,Monoedukation,0.07,7,Schule und Gesellschaft,gering,0.9235929535014257,False,-0.07336449853523312,0.07,0.41678484019606843,58,36,gering+ +6.17,Schulwechsel,-0.38,6,Elternhaus und Familie,negativ,0.6384645645869302,False,-0.07336449853523312,0.38,-0.4159072056249074,29,37,gering− +5.38,kognitive Dispositionen (Prokrastination),-0.41,5,Lernende,negativ,0.6059563758389256,False,-0.08917102326885956,0.41,-0.4132420904516396,22,38,mittel− +5.31,negativ-aktivierend (Angst),-0.4,5,Lernende,negativ,0.608739837398373,False,0.4425504899470148,0.4,-0.4099032635874468,25,39,mittel− +6.15,Körperliche Züchtigung,-0.33,6,Elternhaus und Familie,negativ,0.668243380062615,False,-0.07336449853523312,0.33,-0.40753954121134467,32,40,gering− +6.06,Familienstruktur,0.14,6,Elternhaus und Familie,gering,0.8378335891817927,False,-0.059149754607963165,0.14,0.4053656162508216,53,41,gering+ +7.03,Leistungsbezogene Bezahlung,0.05,7,Schule und Gesellschaft,gering,0.9174717855887603,False,-0.07336449853523314,0.05,0.40397154411752395,60,42,gering+ +7.05,Vertragsschulen / Charter-Schulen,0.04,7,Schule und Gesellschaft,gering,0.9128781474571108,False,-0.07336449853523315,0.04,0.396760122048193,63,43,gering+ +6.07,Geschieden,-0.26,6,Elternhaus und Familie,negativ,0.7061030196788244,False,-0.08431062057732165,0.26,-0.3938138908860457,36,44,gering− +7.08,Dauer der Sommerferien,-0.09,7,Schule und Gesellschaft,negativ,0.8327092370446383,False,-0.08477035593782843,0.09,-0.37867559681950863,56,45,gering− +5.32,negativ-aktivierend (Depressionen),-0.3,5,Lernende,negativ,0.6266841317365268,False,0.4425504899470148,0.3,-0.3713230886031487,34,46,gering− +6.18,Fernsehen,-0.15,6,Elternhaus und Familie,negativ,0.7598477443369885,False,-0.059149754607963165,0.15,-0.3692270873626853,52,47,gering− +6.13,Elterliche Autonomieunterstützung (Familienhilfe),0.06,6,Elternhaus und Familie,gering,0.8320762909326382,False,-0.07336449853523312,0.06,0.3639433328972249,59,48,gering+ +6.02,Bezug staatlicher Transferleistungen,-0.12,6,Elternhaus und Familie,negativ,0.772215227319828,False,-0.07336449853523314,0.12,-0.3613193752948617,54,49,gering− +6.05,Einwanderungsstatus,0.05,6,Elternhaus und Familie,gering,0.8306537734304565,False,-0.07336449853523312,0.05,0.35839658489477866,60,50,gering+ +6.03,Erwerbstätigkeit der Mutter,0.05,6,Elternhaus und Familie,gering,0.8306537734304565,False,-0.08585665050056006,0.05,0.35839658489477866,60,50,gering+ +6.04,Stipendien,0.03,6,Elternhaus und Familie,gering,0.82487518707422,False,-0.07336449853523311,0.03,0.3457631263876847,65,52,gering+ +5.17,Beharrlichkeit und Zuversicht (Konzentration / Ausdauer und Engagement),0.41,5,Lernende,mittel,0.4177336747759273,False,-0.3218185254749976,0.41,0.31443492230202125,22,53,mittel+ +5.12,Kreativität und Lernleistung in Beziehung setzen,0.4,5,Lernende,mittel,0.3880890052355992,False,-0.14757153731358338,0.4,0.2940730063829064,25,54,mittel+ +5.34,negativ-aktivierend (Frustration),-0.04,5,Lernende,negativ,0.6507201646090555,False,0.6056986180050421,0.04,-0.25914076079822995,63,55,gering− +5.22,Perfektionismus,-0.03,5,Lernende,negativ,0.6501826722338218,False,-0.07336449853523314,0.03,-0.2540586051432589,65,56,gering− +5.09,Gekreuzte Lateralität,-0.03,5,Lernende,negativ,0.6501826722338218,False,-0.07336449853523311,0.03,-0.2540586051432589,65,56,gering− +5.25,Emotionen (Wohlbefinden),0.08,5,Lernende,gering,0.5842661691542289,False,-0.09745012386374446,0.08,0.24345585549171578,57,58,gering+ +5.35,negativ-aktivierend (Aggression und Gewalt),0.03,5,Lernende,gering,0.6225686498855841,False,0.30890529874855743,0.03,0.23956267381276325,65,59,gering+ +5.3,Positiv-aktivierend (Entspannung),0.16,5,Lernende,gering,0.48663294797687695,False,0.7827951695376463,0.16,0.23060347176021956,51,60,gering+ +5.03,Beziehung zwischen Schul- und Berufsleistungen,0.37,5,Lernende,gering,0.2835203366058893,False,-0.17512188415193075,0.37,0.2247798719940885,30,61,gering+ +5.21,Schülerpersönlichkeit,0.18,5,Lernende,gering,0.45180722891566244,False,-0.07336449853523312,0.18,0.22192177322024864,48,62,gering+ +5.37,kognitive Dispositionen (Morgentypus vs. Abendtypus),0.18,5,Lernende,gering,0.45180722891566244,False,-0.09227886985139404,0.18,0.22192177322024864,48,62,gering+ +5.14,Beharrlichkeit und Zuversicht (Mindset),0.19,5,Lernende,gering,0.42788461538461536,False,-0.33543250302056493,0.19,0.21416364030433988,46,64,gering+ +5.08,Vorschulische nicht-kognitive Fähigkeiten,0.2,5,Lernende,gering,0.4001572327044027,False,-0.09398809922530565,0.2,0.20440820067160598,44,65,gering+ +5.27,Positiv-aktivierend (Hoffnung),0.2,5,Lernende,gering,0.4001572327044027,False,0.7827951695376463,0.2,0.20440820067160598,44,65,gering+ +5.16,Beharrlichkeit und Zuversicht (Durchhaltevermögen),0.35,5,Lernende,gering,0.200073637702503,False,-0.33543250302056504,0.35,0.17137467759664005,31,67,gering+ +5.15,Beharrlichkeit und Zuversicht (Achtsamkeit),0.26,5,Lernende,gering,0.16621376811594119,False,-0.33543250302056515,0.26,0.1104,36,68,gering+ diff --git a/export/tests_summary.json b/export/tests_summary.json new file mode 100644 index 0000000..fab7a99 --- /dev/null +++ b/export/tests_summary.json @@ -0,0 +1,13 @@ +{ + "silhouette_global": 0.6815565731142729, + "levene_W": 6.124238885035784, + "levene_p": 0.0036581487538003225, + "kruskal_H": 6.095304064204115, + "kruskal_p": 0.04747025227129131, + "kruskal_eps2": 0.06112394125677784, + "spearman_rho_text_d": -0.27386411336988703, + "spearman_p_text_d": 0.023829588725956963, + "chi2": 16.792602473926756, + "chi2_p": 0.010076456895569883, + "chi2_df": 6 +} \ No newline at end of file diff --git a/export/werte_all.json b/export/werte_all.json new file mode 100644 index 0000000..0f2ab87 --- /dev/null +++ b/export/werte_all.json @@ -0,0 +1,922 @@ +{ + "meta": { + "k": 4, + "kapitel": null, + "theme": "dark" + }, + "data": [ + { + "Thermometer_ID": "5.01", + "Stichwort": "Vorausgehende Fähigkeiten & Intelligenz", + "Effektstärke": 0.96, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.08735409337133096, + "Outlier_IQR": false, + "Silhouette_point": 0.6502913752913749 + }, + { + "Thermometer_ID": "5.02", + "Stichwort": "Vorausgehendes Leistungsniveau", + "Effektstärke": 0.73, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.07336449853523334, + "Outlier_IQR": false, + "Silhouette_point": 0.7260754716981136 + }, + { + "Thermometer_ID": "5.03", + "Stichwort": "Beziehung zwischen Schul- und Berufsleistungen", + "Effektstärke": 0.37, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.17512188415193075, + "Outlier_IQR": false, + "Silhouette_point": 0.2835203366058893 + }, + { + "Thermometer_ID": "5.04", + "Stichwort": "Beziehung zwischen Schul- und Universitätsleistungen", + "Effektstärke": 0.55, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.175121884151931, + "Outlier_IQR": false, + "Silhouette_point": 0.680520117762513 + }, + { + "Thermometer_ID": "5.05", + "Stichwort": "Erkenntnisstufen", + "Effektstärke": 1.28, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.05914975460796303, + "Outlier_IQR": true, + "Silhouette_point": 0.5057964601769913 + }, + { + "Thermometer_ID": "5.06", + "Stichwort": "Exekutive Funktionen", + "Effektstärke": 0.62, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.05914975460796313, + "Outlier_IQR": false, + "Silhouette_point": 0.7281195079086115 + }, + { + "Thermometer_ID": "5.07", + "Stichwort": "Stärke des Arbeitsgedächtnisses", + "Effektstärke": 0.63, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.07892435655637323, + "Outlier_IQR": false, + "Silhouette_point": 0.7313852813852812 + }, + { + "Thermometer_ID": "5.08", + "Stichwort": "Vorschulische nicht-kognitive Fähigkeiten", + "Effektstärke": 0.2, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.09398809922530565, + "Outlier_IQR": false, + "Silhouette_point": 0.4001572327044027 + }, + { + "Thermometer_ID": "5.09", + "Stichwort": "Gekreuzte Lateralität", + "Effektstärke": -0.03, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": -0.07336449853523311, + "Outlier_IQR": false, + "Silhouette_point": 0.6501826722338218 + }, + { + "Thermometer_ID": "5.1", + "Stichwort": "Feldunabhängigkeit", + "Effektstärke": 0.94, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.05914975460796317, + "Outlier_IQR": false, + "Silhouette_point": 0.6583828775267535 + }, + { + "Thermometer_ID": "5.11", + "Stichwort": "Beurteilung der eigenen Leistungsfähigkeit", + "Effektstärke": 0.96, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.08382750975171666, + "Outlier_IQR": false, + "Silhouette_point": 0.6502913752913749 + }, + { + "Thermometer_ID": "5.12", + "Stichwort": "Kreativität und Lernleistung in Beziehung setzen", + "Effektstärke": 0.4, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.14757153731358338, + "Outlier_IQR": false, + "Silhouette_point": 0.3880890052355992 + }, + { + "Thermometer_ID": "5.13", + "Stichwort": "Kritisches Denken", + "Effektstärke": 0.84, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.6930555555555555 + }, + { + "Thermometer_ID": "5.14", + "Stichwort": "Beharrlichkeit und Zuversicht (Mindset)", + "Effektstärke": 0.19, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.33543250302056493, + "Outlier_IQR": false, + "Silhouette_point": 0.42788461538461536 + }, + { + "Thermometer_ID": "5.15", + "Stichwort": "Beharrlichkeit und Zuversicht (Achtsamkeit)", + "Effektstärke": 0.26, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.33543250302056515, + "Outlier_IQR": false, + "Silhouette_point": 0.16621376811594119 + }, + { + "Thermometer_ID": "5.16", + "Stichwort": "Beharrlichkeit und Zuversicht (Durchhaltevermögen)", + "Effektstärke": 0.35, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.33543250302056504, + "Outlier_IQR": false, + "Silhouette_point": 0.200073637702503 + }, + { + "Thermometer_ID": "5.17", + "Stichwort": "Beharrlichkeit und Zuversicht (Konzentration / Ausdauer und Engagement)", + "Effektstärke": 0.41, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.3218185254749976, + "Outlier_IQR": false, + "Silhouette_point": 0.4177336747759273 + }, + { + "Thermometer_ID": "5.18", + "Stichwort": "Beharrlichkeit und Zuversicht (Selbstwirksamkeitserwartung)", + "Effektstärke": 0.64, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.3354325030205649, + "Outlier_IQR": false, + "Silhouette_point": 0.7331058020477826 + }, + { + "Thermometer_ID": "5.19", + "Stichwort": "Beharrlichkeit und Zuversicht (Positives Selbstbild)", + "Effektstärke": 0.51, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.3021008406853567, + "Outlier_IQR": false, + "Silhouette_point": 0.6344374342797038 + }, + { + "Thermometer_ID": "5.2", + "Stichwort": "Beharrlichkeit und Zuversicht (Selbstkontrolle)", + "Effektstärke": 0.66, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.3354325030205649, + "Outlier_IQR": false, + "Silhouette_point": 0.7335820895522391 + }, + { + "Thermometer_ID": "5.21", + "Stichwort": "Schülerpersönlichkeit", + "Effektstärke": 0.18, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.45180722891566244 + }, + { + "Thermometer_ID": "5.22", + "Stichwort": "Perfektionismus", + "Effektstärke": -0.03, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.6501826722338218 + }, + { + "Thermometer_ID": "5.23", + "Stichwort": "Emotionen", + "Effektstärke": 0.61, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.1013942427833901, + "Outlier_IQR": false, + "Silhouette_point": 0.7232381801962534 + }, + { + "Thermometer_ID": "5.24", + "Stichwort": "Emotionen (Emotionale Intelligenz)", + "Effektstärke": 0.5, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": -0.1010290138898771, + "Outlier_IQR": false, + "Silhouette_point": 0.6195931477516049 + }, + { + "Thermometer_ID": "5.25", + "Stichwort": "Emotionen (Wohlbefinden)", + "Effektstärke": 0.08, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.09745012386374446, + "Outlier_IQR": false, + "Silhouette_point": 0.5842661691542289 + }, + { + "Thermometer_ID": "5.26", + "Stichwort": "Positiv-aktivierend (Freude)", + "Effektstärke": 0.5, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": 0.7827951695376463, + "Outlier_IQR": false, + "Silhouette_point": 0.6195931477516049 + }, + { + "Thermometer_ID": "5.27", + "Stichwort": "Positiv-aktivierend (Hoffnung)", + "Effektstärke": 0.2, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": 0.7827951695376463, + "Outlier_IQR": false, + "Silhouette_point": 0.4001572327044027 + }, + { + "Thermometer_ID": "5.28", + "Stichwort": "Positiv-aktivierend (Neugierde)", + "Effektstärke": 0.74, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "hoch", + "Text_Dimension": 0.5855547077500068, + "Outlier_IQR": false, + "Silhouette_point": 0.7238450074515644 + }, + { + "Thermometer_ID": "5.29", + "Stichwort": "Positiv-aktivierend (Glücklichsein)", + "Effektstärke": 0.54, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "mittel", + "Text_Dimension": 0.7827951695376463, + "Outlier_IQR": false, + "Silhouette_point": 0.6708582834331347 + }, + { + "Thermometer_ID": "5.3", + "Stichwort": "Positiv-aktivierend (Entspannung)", + "Effektstärke": 0.16, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": 0.7827951695376463, + "Outlier_IQR": false, + "Silhouette_point": 0.48663294797687695 + }, + { + "Thermometer_ID": "5.31", + "Stichwort": "negativ-aktivierend (Angst)", + "Effektstärke": -0.4, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": 0.4425504899470148, + "Outlier_IQR": false, + "Silhouette_point": 0.608739837398373 + }, + { + "Thermometer_ID": "5.32", + "Stichwort": "negativ-aktivierend (Depressionen)", + "Effektstärke": -0.3, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": 0.4425504899470148, + "Outlier_IQR": false, + "Silhouette_point": 0.6266841317365268 + }, + { + "Thermometer_ID": "5.33", + "Stichwort": "negativ-aktivierend (Wut)", + "Effektstärke": -0.65, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": 0.44255048994701485, + "Outlier_IQR": true, + "Silhouette_point": 0.510747535596933 + }, + { + "Thermometer_ID": "5.34", + "Stichwort": "negativ-aktivierend (Frustration)", + "Effektstärke": -0.04, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": 0.6056986180050421, + "Outlier_IQR": false, + "Silhouette_point": 0.6507201646090555 + }, + { + "Thermometer_ID": "5.35", + "Stichwort": "negativ-aktivierend (Aggression und Gewalt)", + "Effektstärke": 0.03, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": 0.30890529874855743, + "Outlier_IQR": false, + "Silhouette_point": 0.6225686498855841 + }, + { + "Thermometer_ID": "5.36", + "Stichwort": "negativ-aktivierend (Langeweile)", + "Effektstärke": -0.46, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": 0.4425504899470148, + "Outlier_IQR": false, + "Silhouette_point": 0.5871794871794872 + }, + { + "Thermometer_ID": "5.37", + "Stichwort": "kognitive Dispositionen (Morgentypus vs. Abendtypus)", + "Effektstärke": 0.18, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "gering", + "Text_Dimension": -0.09227886985139404, + "Outlier_IQR": false, + "Silhouette_point": 0.45180722891566244 + }, + { + "Thermometer_ID": "5.38", + "Stichwort": "kognitive Dispositionen (Prokrastination)", + "Effektstärke": -0.41, + "Kapitel": 5, + "Kapitelname": "Lernende", + "Bin": "negativ", + "Text_Dimension": -0.08917102326885956, + "Outlier_IQR": false, + "Silhouette_point": 0.6059563758389256 + }, + { + "Thermometer_ID": "6.01", + "Stichwort": "Sozioökonomischer Status", + "Effektstärke": 0.56, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "mittel", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.6827943498774371 + }, + { + "Thermometer_ID": "6.02", + "Stichwort": "Bezug staatlicher Transferleistungen", + "Effektstärke": -0.12, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "negativ", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.772215227319828 + }, + { + "Thermometer_ID": "6.03", + "Stichwort": "Erwerbstätigkeit der Mutter", + "Effektstärke": 0.05, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.08585665050056006, + "Outlier_IQR": false, + "Silhouette_point": 0.8306537734304565 + }, + { + "Thermometer_ID": "6.04", + "Stichwort": "Stipendien", + "Effektstärke": 0.03, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523311, + "Outlier_IQR": false, + "Silhouette_point": 0.82487518707422 + }, + { + "Thermometer_ID": "6.05", + "Stichwort": "Einwanderungsstatus", + "Effektstärke": 0.05, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.8306537734304565 + }, + { + "Thermometer_ID": "6.06", + "Stichwort": "Familienstruktur", + "Effektstärke": 0.14, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.059149754607963165, + "Outlier_IQR": false, + "Silhouette_point": 0.8378335891817927 + }, + { + "Thermometer_ID": "6.07", + "Stichwort": "Geschieden", + "Effektstärke": -0.26, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "negativ", + "Text_Dimension": -0.08431062057732165, + "Outlier_IQR": false, + "Silhouette_point": 0.7061030196788244 + }, + { + "Thermometer_ID": "6.08", + "Stichwort": "nicht-geschieden vs. wiederverheiratet", + "Effektstärke": 0.24, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.09341002740120666, + "Outlier_IQR": false, + "Silhouette_point": 0.8324161165454884 + }, + { + "Thermometer_ID": "6.09", + "Stichwort": "Adoption", + "Effektstärke": 0.21, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.0733644985352332, + "Outlier_IQR": false, + "Silhouette_point": 0.8381014976799916 + }, + { + "Thermometer_ID": "6.1", + "Stichwort": "Kinderheime", + "Effektstärke": 0.33, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.8071221370551951 + }, + { + "Thermometer_ID": "6.11", + "Stichwort": "Häusliches Anregungsniveau", + "Effektstärke": 0.4, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "mittel", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.7824192735239991 + }, + { + "Thermometer_ID": "6.12", + "Stichwort": "Elternunterstützung beim Lernen", + "Effektstärke": 0.41, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "mittel", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.77761147335638 + }, + { + "Thermometer_ID": "6.13", + "Stichwort": "Elterliche Autonomieunterstützung (Familienhilfe)", + "Effektstärke": 0.06, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.8320762909326382 + }, + { + "Thermometer_ID": "6.14", + "Stichwort": "Elternerwartungen", + "Effektstärke": 0.49, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "mittel", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.7301343867848403 + }, + { + "Thermometer_ID": "6.15", + "Stichwort": "Körperliche Züchtigung", + "Effektstärke": -0.33, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "negativ", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.668243380062615 + }, + { + "Thermometer_ID": "6.16", + "Stichwort": "Väter", + "Effektstärke": 0.21, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.8381014976799916 + }, + { + "Thermometer_ID": "6.17", + "Stichwort": "Schulwechsel", + "Effektstärke": -0.38, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "negativ", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.6384645645869302 + }, + { + "Thermometer_ID": "6.18", + "Stichwort": "Fernsehen", + "Effektstärke": -0.15, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "negativ", + "Text_Dimension": -0.059149754607963165, + "Outlier_IQR": false, + "Silhouette_point": 0.7598477443369885 + }, + { + "Thermometer_ID": "6.19", + "Stichwort": "Programme für Eltern", + "Effektstärke": 0.39, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.7865197915104075 + }, + { + "Thermometer_ID": "6.2", + "Stichwort": "Hausbesuche durch Lehrpersonen", + "Effektstärke": 0.22, + "Kapitel": 6, + "Kapitelname": "Elternhaus und Familie", + "Bin": "gering", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.8366902739695423 + }, + { + "Thermometer_ID": "7.01", + "Stichwort": "Finanzielle Ausstattung", + "Effektstärke": 0.19, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.9251410228772312 + }, + { + "Thermometer_ID": "7.02", + "Stichwort": "Accountability / Rechenschaftspflicht", + "Effektstärke": 0.27, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.0733644985352329, + "Outlier_IQR": false, + "Silhouette_point": 0.8900556973256574 + }, + { + "Thermometer_ID": "7.03", + "Stichwort": "Leistungsbezogene Bezahlung", + "Effektstärke": 0.05, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.9174717855887603 + }, + { + "Thermometer_ID": "7.04", + "Stichwort": "Qualität des Schulgebäudes", + "Effektstärke": 0.24, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07892435655637318, + "Outlier_IQR": false, + "Silhouette_point": 0.9083468761774898 + }, + { + "Thermometer_ID": "7.05", + "Stichwort": "Vertragsschulen / Charter-Schulen", + "Effektstärke": 0.04, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523315, + "Outlier_IQR": false, + "Silhouette_point": 0.9128781474571108 + }, + { + "Thermometer_ID": "7.06", + "Stichwort": "Konfessionsschulen", + "Effektstärke": 0.23, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.9129250266162217 + }, + { + "Thermometer_ID": "7.07", + "Stichwort": "Monoedukation", + "Effektstärke": 0.07, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523312, + "Outlier_IQR": false, + "Silhouette_point": 0.9235929535014257 + }, + { + "Thermometer_ID": "7.08", + "Stichwort": "Dauer der Sommerferien", + "Effektstärke": -0.09, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "negativ", + "Text_Dimension": -0.08477035593782843, + "Outlier_IQR": false, + "Silhouette_point": 0.8327092370446383 + }, + { + "Thermometer_ID": "7.09", + "Stichwort": "Sommerschulen", + "Effektstärke": 0.17, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523315, + "Outlier_IQR": false, + "Silhouette_point": 0.9281937118300768 + }, + { + "Thermometer_ID": "7.1", + "Stichwort": "Schulkalender / Stundenplan", + "Effektstärke": 0.1, + "Kapitel": 7, + "Kapitelname": "Schule und Gesellschaft", + "Bin": "gering", + "Text_Dimension": -0.07336449853523314, + "Outlier_IQR": false, + "Silhouette_point": 0.9281794453692277 + } + ], + "deskriptiv": [ + { + "Gruppe": "Gesamt", + "n": 68, + "mean": 0.2458823529411765, + "std": 0.37290403428819446, + "min": -0.65, + "q1": 0.0475, + "median": 0.21, + "q3": 0.5, + "max": 1.28, + "skew": 0.12925197443518738, + "kurtosis": 0.2800914374662091 + }, + { + "Gruppe": "Kapitel 5", + "n": 38, + "mean": 0.3394736842105263, + "std": 0.43649059535035, + "min": -0.65, + "q1": 0.1, + "median": 0.385, + "q3": 0.6275, + "max": 1.28, + "skew": -0.29469572711835434, + "kurtosis": -0.11719052918009742 + }, + { + "Gruppe": "Kapitel 6", + "n": 20, + "mean": 0.1275, + "std": 0.27132957236929645, + "min": -0.38, + "q1": -0.0075, + "median": 0.175, + "q3": 0.34500000000000003, + "max": 0.56, + "skew": -0.3580457406718272, + "kurtosis": -0.6941765728667146 + }, + { + "Gruppe": "Kapitel 7", + "n": 10, + "mean": 0.127, + "std": 0.11264990013311152, + "min": -0.09, + "q1": 0.05500000000000001, + "median": 0.135, + "q3": 0.22, + "max": 0.27, + "skew": -0.5585413007473659, + "kurtosis": -0.22749527826960758 + } + ], + "cluster": { + "silhouette_global": 0.6815565731142729, + "centers_full": [ + [ + 0.6542857142857142, + 1.0, + 1.1102230246251565e-16, + 5.551115123125783e-17 + ], + [ + 0.1275, + 1.1102230246251565e-16, + 0.9999999999999998, + 2.7755575615628914e-17 + ], + [ + -0.04941176470588232, + 1.0000000000000002, + 5.551115123125783e-17, + 2.7755575615628914e-17 + ], + [ + 0.127, + 1.1102230246251565e-16, + 0.0, + 0.9999999999999999 + ] + ], + "centers_effekt_only": [ + 0.6542857142857142, + 0.1275, + -0.04941176470588232, + 0.127 + ] + }, + "profiles": [ + { + "Cluster": 0, + "n": 21, + "Ø d": 0.654, + "Kapitelverteilung": { + "5": 21 + }, + "Top_Terme": [ + "und", + "zuversicht", + "beharrlichkeit" + ] + }, + { + "Cluster": 1, + "n": 20, + "Ø d": 0.128, + "Kapitelverteilung": { + "6": 20 + }, + "Top_Terme": [ + "geschieden", + "kinderheime", + "stipendien" + ] + }, + { + "Cluster": 2, + "n": 17, + "Ø d": -0.049, + "Kapitelverteilung": { + "5": 17 + }, + "Top_Terme": [ + "aktivierend", + "negativ", + "kognitive" + ] + }, + { + "Cluster": 3, + "n": 10, + "Ø d": 0.127, + "Kapitelverteilung": { + "7": 10 + }, + "Top_Terme": [ + "konfessionsschulen", + "sommerschulen", + "monoedukation" + ] + } + ], + "tests_summary": { + "silhouette_global": 0.6815565731142729, + "levene_W": 6.124238885035784, + "levene_p": 0.0036581487538003225, + "kruskal_H": 6.095304064204115, + "kruskal_p": 0.04747025227129131, + "kruskal_eps2": 0.06112394125677784, + "spearman_rho_text_d": -0.27386411336988703, + "spearman_p_text_d": 0.023829588725956963, + "chi2": 16.792602473926756, + "chi2_p": 0.010076456895569883, + "chi2_df": 6 + }, + "data_quality": { + "n_rows": 68, + "duplicate_ids": {}, + "n_duplicates": 0, + "invalid_kapitel_entries": [], + "effekt_min": -0.65, + "effekt_max": 1.28, + "empty_stichwort": 0 + } +} \ No newline at end of file diff --git a/visible-learning.py b/visible-learning.py new file mode 100644 index 0000000..10e3bd6 --- /dev/null +++ b/visible-learning.py @@ -0,0 +1,1001 @@ +from __future__ import annotations +""" +Visible Learning – Explorative Clusteranalyse +--------------------------------------------- +CI: angelehnt an simulation_bildungswirkgefuege +Funktion: CSV mit Effektstärken laden, manuelle Bins + K-Means-Cluster bilden, + Silhouette-Score berechnen und Visualisierungen erzeugen. +""" + +# ----------------------------------------- +# Imports +# ----------------------------------------- +import os +import math +import pandas as pd +import numpy as np +import json + +from sklearn.preprocessing import OneHotEncoder +from sklearn.cluster import KMeans +from sklearn.metrics import silhouette_score +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.decomposition import PCA +from sklearn.metrics import silhouette_score, silhouette_samples +from scipy import stats +from scipy.stats import anderson, kruskal, levene, spearmanr + +import plotly.graph_objs as go +import plotly.io as pio + +# ----------------------------------------- +# Konfiguration laden +# ----------------------------------------- +from config_visible_learning import ( + csv_file, + k_clusters, + export_fig_visual, + export_fig_png, + theme, + selected_kapitel, + analyse_all, + export_werte_all, +) + +# ----------------------------------------- +# Template/CI +# ----------------------------------------- +try: + from ci_template import plotly_template + plotly_template.set_theme(theme) + _ci_layout = lambda title, x, y: plotly_template.get_standard_layout( + title=title, x_title=x, y_title=y + ) +except Exception: + # Fallback: neutrale Plotly-Defaults + _ci_layout = lambda title, x, y: dict( + title=title, xaxis_title=x, yaxis_title=y + ) + +# ----------------------------------------- +# Export-Helfer (HTML/PNG) – CI-kompatibel +# ----------------------------------------- +EXPORT_DIR = os.path.join(os.path.dirname(__file__), "export") +if not os.path.exists(EXPORT_DIR): + try: + os.makedirs(EXPORT_DIR, exist_ok=True) + except Exception: + pass + + +def export_figure(fig, name: str, do_html: bool, do_png: bool): + """Exportiert eine Plotly-Figur gemäß CI-Flags.""" + base = os.path.join(EXPORT_DIR, name) + if do_html: + p = f"{base}.html" + pio.write_html(fig, file=p, auto_open=False, include_plotlyjs="cdn") + if do_png: + try: + p = f"{base}.png" + pio.write_image(fig, p, scale=2) + except Exception: + # PNG erfordert Kaleido; leise ignorieren, wenn nicht installiert + pass + +# ----------------------------------------- +# Daten | Laden & Vorbereiten +# ----------------------------------------- + +REQUIRED_COLS = ["Thermometer_ID", "Stichwort", "Effektstärke"] + + +def load_data(csv_path: str) -> pd.DataFrame: + df = pd.read_csv(csv_path) + missing = [c for c in REQUIRED_COLS if c not in df.columns] + if missing: + raise ValueError(f"Fehlende Spalten in CSV: {missing}") + # Typen bereinigen + df["Thermometer_ID"] = df["Thermometer_ID"].astype(str) + # Effektstärke robust in float wandeln + df["Effektstärke"] = ( + df["Effektstärke"].astype(str).str.replace(",", ".", regex=False).str.strip() + ) + df["Effektstärke"] = pd.to_numeric(df["Effektstärke"], errors="coerce") + + # Kapitel aus Thermometer_ID ableiten und Kapitelname mappen + df["Kapitel"] = df["Thermometer_ID"].astype(str).str.split(".").str[0].astype(int) + + kapitel_map = { + 5: "Lernende", + 6: "Elternhaus und Familie", + 7: "Schule und Gesellschaft", + 8: "Klassenzimmer", + 9: "Lehrperson", + 10: "Curriculum", + 11: "Zielorientiertes Unterrichten", + 12: "Lernstrategien", + 13: "Lehrstrategien", + 14: "Nutzung von Technologien", + 15: "Schulische und außerschulische Einflüsse", + } + df["Kapitelname"] = df["Kapitel"].map(kapitel_map).fillna(df["Kapitel"].map(lambda k: f"Kapitel {k}")) + + return df.dropna(subset=["Effektstärke"]) # nur gültige Zahlen + +def validate_data(df: pd.DataFrame) -> dict: + """Einfache Datenvalidierung und Qualitätsreport. + Hinweis: Fehlende Effektstärken wurden in load_data bereits entfernt. + """ + report = {} + report["n_rows"] = int(len(df)) + # Duplikate + dup_counts = df["Thermometer_ID"].value_counts() + duplicates = dup_counts[dup_counts > 1] + report["duplicate_ids"] = duplicates.to_dict() + report["n_duplicates"] = int(duplicates.sum()) if not duplicates.empty else 0 + # Gültige Kapitel (5..15) + valid_kap = set(range(5, 16)) + invalid_kapitel = df.loc[~df["Kapitel"].isin(valid_kap), "Kapitel"].tolist() + report["invalid_kapitel_entries"] = invalid_kapitel + # Wertebereich d + report["effekt_min"] = float(df["Effektstärke"].min()) if not df.empty else None + report["effekt_max"] = float(df["Effektstärke"].max()) if not df.empty else None + # Leere Stichworte + empty_keywords = df["Stichwort"].astype(str).str.strip().eq("").sum() + report["empty_stichwort"] = int(empty_keywords) + return report + +# ----------------------------------------- +# Manuelle Bins (heuristische Einteilung) +# ----------------------------------------- +BIN_LABELS = ["negativ", "gering", "mittel", "hoch"] + + +def manual_bin(d: float) -> str: + if d < 0: + return "negativ" + if 0 <= d < 0.40: + return "gering" + if 0.40 <= d < 0.70: + return "mittel" + return "hoch" + + +def add_manual_bins(df: pd.DataFrame) -> pd.DataFrame: + df = df.copy() + df["Bin"] = df["Effektstärke"].apply(manual_bin) + return df + + +# ----------------------------------------- +# K-Means-Clustering (Effektstärke + Kapitel) +# ----------------------------------------- + +def encode_features(df: pd.DataFrame) -> tuple[np.ndarray, list[str]]: + """One-Hot-Encoding des Kapitels + Effektstärke (metrisch).""" + try: + enc = OneHotEncoder(sparse_output=False, handle_unknown="ignore") # neuere sklearn-Versionen + except TypeError: + enc = OneHotEncoder(sparse=False, handle_unknown="ignore") # ältere sklearn-Versionen + cat = df[["Kapitel"]].fillna(-1) + cat_ohe = enc.fit_transform(cat) + eff = df[["Effektstärke"]].values + X = np.hstack([eff, cat_ohe]) + feature_names = ["Effektstärke"] + [f"kap::{c}" for c in enc.get_feature_names_out(["Kapitel"])] + return X, feature_names + + +def encode_features_3d(df: pd.DataFrame) -> tuple[np.ndarray, list[str]]: + """Effektstärke + Kapitel + Textdimension (TF-IDF + PCA) für 3D-Clustering.""" + # Kapitel + try: + enc = OneHotEncoder(sparse_output=False, handle_unknown="ignore") + except TypeError: + enc = OneHotEncoder(sparse=False, handle_unknown="ignore") + cat = df[["Kapitel"]].fillna(-1) + cat_ohe = enc.fit_transform(cat) + + # Effektstärke + eff = df[["Effektstärke"]].values + + # Textdimension über TF-IDF + PCA + vectorizer = TfidfVectorizer(max_features=100) + X_text = vectorizer.fit_transform(df["Stichwort"].astype(str)) + pca = PCA(n_components=1, random_state=42) + text_dim = pca.fit_transform(X_text.toarray()) + + # Textdimension im DataFrame speichern + df["Text_Dimension"] = text_dim.flatten() + + # Zusammenführen + X = np.hstack([eff, cat_ohe, text_dim]) + feature_names = ["Effektstärke"] + list(enc.get_feature_names_out(["Kapitel"])) + ["Text_Dimension"] + return X, feature_names + + +def run_kmeans(df: pd.DataFrame, k: int = 4, random_state: int = 42): + X, feature_names = encode_features(df) + model = KMeans(n_clusters=k, n_init=20, random_state=random_state) + labels = model.fit_predict(X) + sil = silhouette_score(X, labels) if k > 1 and len(df) > k else np.nan + return labels, sil, model + +# ----------------------------------------- +# Statistische Auswertungen +# ----------------------------------------- + +def describe_effects(df: pd.DataFrame) -> pd.DataFrame: + """Deskriptive Statistik (gesamt & je Kapitel).""" + # Aggregation für Kapitel mit eindeutigen Spaltennamen + by_kap = df.groupby("Kapitel")["Effektstärke"].agg( + n="count", + mean="mean", + std=lambda s: s.std(ddof=1), + min="min", + q1=lambda s: s.quantile(0.25), + median="median", + q3=lambda s: s.quantile(0.75), + max="max", + skew="skew", + kurtosis=lambda s: s.kurt(), + ) + # Gesamtzeile + overall = pd.DataFrame({ + "n": [df["Effektstärke"].count()], + "mean": [df["Effektstärke"].mean()], + "std": [df["Effektstärke"].std(ddof=1)], + "min": [df["Effektstärke"].min()], + "q1": [df["Effektstärke"].quantile(0.25)], + "median": [df["Effektstärke"].median()], + "q3": [df["Effektstärke"].quantile(0.75)], + "max": [df["Effektstärke"].max()], + "skew": [df["Effektstärke"].skew()], + "kurtosis": [df["Effektstärke"].kurt()], + }, index=["Gesamt"]) + # Kapitel-Index schöner beschriften + by_kap.index = [f"Kapitel {int(k)}" for k in by_kap.index] + # Zusammenführen + out = pd.concat([overall, by_kap]) + return out + +def plot_table_stats(stats_df: pd.DataFrame, title: str): + from plotly.graph_objs import Table, Figure + colors = plotly_template.get_colors() + fig = Figure(data=[Table( + header=dict(values=[""] + list(stats_df.columns), + fill_color=colors["brightArea"], font=dict(color=colors["white"])), + cells=dict(values=[stats_df.index.astype(str)] + [stats_df[c].round(3).tolist() for c in stats_df.columns], + fill_color=colors["depthArea"], font=dict(color=colors["white"])) + )]) + fig.update_layout(plotly_template.get_standard_layout(title, "", "")) + fig.show() + +def normality_and_qq(df: pd.DataFrame, kapitel: int | None = None): + x = df["Effektstärke"].dropna().values + ad = anderson(x, dist='norm') + print(f"Anderson–Darling: A2={ad.statistic:.3f} | kritische Werte {ad.critical_values} | Sig-Level {ad.significance_level}") + # QQ-Plot + if len(x) < 3: + print("QQ-Plot: Zu wenige Datenpunkte (<3) – Plot wird übersprungen.") + return + styles = plotly_template.get_plot_styles() + osm, osr = stats.probplot(x, dist="norm", rvalue=False) + # Kompatibel zu unterschiedlichen SciPy-Versionen: + # Variante A: osm=array(theoretische Quantile), osr=array(ordered responses) + # Variante B: osm=(array(theoretische Quantile), array(ordered responses)), osr=... (ungleich genutzt) + if isinstance(osm, (tuple, list)) and len(osm) == 2 and np.ndim(osm[0]) == 1: + th = np.asarray(osm[0]) + ord_data = np.asarray(osm[1]) + else: + th = np.asarray(osm) + ord_data = np.asarray(osr) + + # NaNs/Inf filtern und Mindestlänge absichern + mask = np.isfinite(th) & np.isfinite(ord_data) + th = th[mask] + ord_data = ord_data[mask] + if th.size < 2: + print("QQ-Plot: Zu wenige gültige Punkte nach Filter – Fit wird übersprungen.") + # nur Punkte plotten + fig = go.Figure() + fig.add_trace(go.Scatter(x=th, y=ord_data, mode="markers", marker=styles["marker_accent"], name="Daten")) + lab = f"QQ-Plot Effektstärken ({'Kapitel '+str(kapitel) if kapitel else 'Gesamt'})" + fig.update_layout(plotly_template.get_standard_layout(lab, "Theoretische Quantile (Normal)", "Beobachtete Quantile")) + fig.show() + return + + fig = go.Figure() + fig.add_trace(go.Scatter(x=th, y=ord_data, mode="markers", marker=styles["marker_accent"], name="Daten")) + # Diagonale (Least Squares Fit) + m, b = np.polyfit(th, ord_data, 1) + fig.add_trace(go.Scatter(x=th, y=m*th + b, mode="lines", line=styles["linie_primaryLine"], name="Fit")) + lab = f"QQ-Plot Effektstärken ({'Kapitel '+str(kapitel) if kapitel else 'Gesamt'})" + fig.update_layout(plotly_template.get_standard_layout(lab, "Theoretische Quantile (Normal)", "Beobachtete Quantile")) + fig.show() + +def mark_outliers_iqr(df: pd.DataFrame) -> pd.DataFrame: + q1, q3 = df["Effektstärke"].quantile([0.25, 0.75]) + iqr = q3 - q1 + lo, hi = q1 - 1.5*iqr, q3 + 1.5*iqr + out = df.copy() + out["Outlier_IQR"] = ~out["Effektstärke"].between(lo, hi) + print(f"IQR-Grenzen: [{lo:.2f}, {hi:.2f}] | Ausreißer: {int(out['Outlier_IQR'].sum())}") + return out + +def group_tests_by_kapitel(df: pd.DataFrame): + groups = [g.dropna().values for _, g in df.groupby("Kapitel")["Effektstärke"]] + if len(groups) >= 2: + lev = levene(*groups, center='median') + print(f"Levene (Homogenität): W={lev.statistic:.3f}, p={lev.pvalue:.4f}") + # robust: Kruskal–Wallis + if len(groups) >= 2: + kw = kruskal(*groups) + n_total = sum(len(g) for g in groups) + h = kw.statistic + eps2 = (h - (len(groups)-1)) / (n_total - 1) + print(f"Kruskal–Wallis: H={h:.3f}, p={kw.pvalue:.6f} | ε²={eps2:.3f}") + +def text_vs_effect(df: pd.DataFrame): + if "Text_Dimension" not in df.columns: + encode_features_3d(df) + rho, p = spearmanr(df["Text_Dimension"], df["Effektstärke"], nan_policy='omit') + print(f"Spearman ρ(Text, d) = {rho:.3f}, p={p:.6f}") + styles = plotly_template.get_plot_styles() + fig = go.Figure() + fig.add_trace(go.Scatter(x=df["Text_Dimension"], y=df["Effektstärke"], + mode="markers", marker=styles["marker_brightArea"], name="Thermometer", + text=df["Stichwort"], + hovertemplate="Textdim: %{x:.3f}
d: %{y:.2f}
%{text}")) + x = df["Text_Dimension"].values; y = df["Effektstärke"].values + if len(x) >= 2 and np.isfinite(x).all() and np.isfinite(y).all(): + m, b = np.polyfit(x, y, 1) + xx = np.linspace(x.min(), x.max(), 100) + fig.add_trace(go.Scatter(x=xx, y=m*xx+b, mode="lines", line=styles["linie_secondaryLine"], name="Trend")) + fig.update_layout(plotly_template.get_standard_layout("Textdimension × Effektstärke (Spearman)", "Textdimension (PCA1)", "Cohen d")) + fig.show() + +def chi2_bins_kapitel(df: pd.DataFrame): + ct = pd.crosstab(df["Kapitel"], df["Bin"]).reindex(sorted(df["Kapitel"].unique())) + chi2 = stats.chi2_contingency(ct) + print("Kontingenztafel (Kapitel × Bin):") + print(ct) + print(f"Chi²={chi2[0]:.3f}, p={chi2[1]:.6f}, df={chi2[2]} (Unabhängigkeitstest)") + return ct + +def cluster_diagnostics(df: pd.DataFrame, k_min: int = 2, k_max: int = 8): + X, _ = encode_features(df) + inertias, sils, ks = [], [], [] + for k in range(k_min, k_max+1): + km = KMeans(n_clusters=k, n_init=20, random_state=42).fit(X) + inertias.append(km.inertia_) + ks.append(k) + sils.append(silhouette_score(X, km.labels_) if k>1 else np.nan) + colors = plotly_template.get_colors() + fig = go.Figure() + fig.add_trace(go.Scatter(x=ks, y=inertias, mode="lines+markers", + line=dict(color=colors["primaryLine"], width=2), name="Inertia (Elbow)")) + fig.add_trace(go.Scatter(x=ks, y=sils, mode="lines+markers", + line=dict(color=colors["secondaryLine"], width=2), name="Silhouette")) + fig.update_layout(plotly_template.get_standard_layout("Cluster-Diagnostik (k)", "k", "Wert")) + fig.show() + +def cluster_profiles(df: pd.DataFrame, labels: np.ndarray, top_terms: int = 3): + res = [] + tmp = df.copy() + tmp["Cluster"] = labels + vect = TfidfVectorizer(max_features=300) + Xtxt = vect.fit_transform(tmp["Stichwort"].astype(str)) + vocab = np.array(vect.get_feature_names_out()) + for c in sorted(tmp["Cluster"].unique()): + sub = tmp[tmp["Cluster"] == c] + mean_d = sub["Effektstärke"].mean() + n = len(sub) + by_kap = sub["Kapitel"].value_counts().sort_index().to_dict() + # positionsbasierte Zeilenindizes für die Sparse-Matrix + pos_idx = tmp.index.get_indexer(sub.index) + mean_tfidf = np.asarray(Xtxt[pos_idx].mean(axis=0)).ravel() + top_idx = mean_tfidf.argsort()[::-1][:top_terms] + terms = vocab[top_idx].tolist() + res.append({"Cluster": c, "n": n, "Ø d": round(mean_d,3), "Kapitelverteilung": by_kap, "Top_Terme": terms}) + prof = pd.DataFrame(res).set_index("Cluster") + print("\nCluster-Profile:") + print(prof) + return prof + +# ----------------------------------------- +# Signifikanz-geführte Sicht (kapitelunabhängig) +# ----------------------------------------- + +def _minmax_norm(a: np.ndarray) -> np.ndarray: + a = np.asarray(a, dtype=float) + if a.size == 0: + return a + lo, hi = np.nanmin(a), np.nanmax(a) + if not np.isfinite(lo) or not np.isfinite(hi) or hi - lo <= 1e-12: + return np.zeros_like(a) + return (a - lo) / (hi - lo) + +def build_significance_view(df: pd.DataFrame) -> pd.DataFrame: + """ + Erzeugt eine kapitelunabhängige Sicht mit einem 'SignifikanzScore'. + Idee: Kombination aus Effektstärke-Magnitude und (falls vorhanden) individueller Silhouette-Trennschärfe. + - score_basis = |d| (größer = stärker) + - score_cluster = Silhouette_point (kleiner 0 -> auf 0 gesetzt), anschließend min-max-normalisiert + - Gesamt-Score = 0.6*norm(|d|) + 0.4*norm(max(Silhouette_point, 0)) + Vorzeichen des Scores folgt dem Vorzeichen von d, damit negative Effekte unten landen. + """ + tmp = df.copy() + # Basisgrößen + tmp["abs_d"] = tmp["Effektstärke"].abs() + if "Silhouette_point" not in tmp.columns: + tmp["Silhouette_point"] = np.nan + + sil_nonneg = tmp["Silhouette_point"].fillna(0.0).clip(lower=0.0) + score_basis = _minmax_norm(tmp["abs_d"].values) + score_sil = _minmax_norm(sil_nonneg.values) + + score = 0.6 * score_basis + 0.4 * score_sil + tmp["SignifikanzScore"] = score * np.sign(tmp["Effektstärke"].values) + + # Ranglisten (absolut stärkste zuerst) + tmp["Rank_abs"] = (-tmp["abs_d"]).rank(method="min").astype(int) + tmp["Rank_score"] = (-tmp["SignifikanzScore"].abs()).rank(method="min").astype(int) + + # Kategorien für schnelle Filterung + def impact_label(d): + if d >= 0.70: + return "hoch+" + if d >= 0.40: + return "mittel+" + if d >= 0.00: + return "gering+" + if d > -0.40: + return "gering−" + if d > -0.70: + return "mittel−" + return "hoch−" + + tmp["Impact_Label"] = tmp["Effektstärke"].apply(impact_label) + return tmp + +def plot_significance_space(df_sig: pd.DataFrame): + """ + 2D-Signifikanzraum: + x = Effektstärke (Cohen d) + y = SignifikanzScore (vorzeichenbehaftet) + Punktgröße ~ |Score|, Farbe nach Vorzeichen (CI-Farben). + """ + styles = plotly_template.get_plot_styles() + colors = plotly_template.get_colors() + + # Markergrößen (skaliert) + s = (df_sig["SignifikanzScore"].abs() * 20.0) + 6.0 + + # Farben nach Vorzeichen + color_pos = colors.get("positiveHighlight", "#2ca02c") + color_neg = colors.get("negativeHighlight", "#d62728") + point_colors = np.where(df_sig["SignifikanzScore"] >= 0, color_pos, color_neg) + + hovertemplate = ( + "Thermometer: %{customdata[0]}
" + "Stichwort: %{text}
" + "d: %{x:.2f}
" + "Score: %{y:.3f}
" + "Kapitel: %{customdata[1]}
" + "Impact: %{customdata[2]}
" + "Rank(|d|): %{customdata[3]} | Rank(|Score|): %{customdata[4]}" + ) + + fig = go.Figure() + fig.add_trace(go.Scatter( + x=df_sig["Effektstärke"], + y=df_sig["SignifikanzScore"], + mode="markers", + marker=dict(color=point_colors, size=s), + text=df_sig["Stichwort"], + customdata=np.stack([ + df_sig["Thermometer_ID"], + df_sig["Kapitelname"], + df_sig["Impact_Label"], + df_sig["Rank_abs"], + df_sig["Rank_score"], + ], axis=-1), + name="Thermometer", + hovertemplate=hovertemplate + )) + + # Referenzlinien + fig.add_hline(y=0, line=dict(color=colors["border"], width=1)) + for x0 in [0.0, 0.40, 0.70, -0.40, -0.70]: + fig.add_vline(x=x0, line=dict(color=colors["border"], width=1, dash="dot")) + + fig.update_layout(plotly_template.get_standard_layout( + "Signifikanz-geführter Raum: Effektstärke × Score (kapitelunabhängig)", + "Cohen d", "SignifikanzScore" + )) + fig.show() + +# ----------------------------------------- +# Visualisierungen +# ----------------------------------------- + +def plot_heatmap_kapitel_vs_d(df: pd.DataFrame, kapitel: int | None = None, bins_d: int = 30): + """2D-Heatmap (Histogram2d) von Kapitel (x) gegen Effektstärke (y). + - Zeigt die Dichte/Anzahl pro Zelle (Kapitel × d-Bereich) + - CI-Farbskala anhand Template-Farben (depthArea → brightArea) + """ + colors = plotly_template.get_colors() + styles = plotly_template.get_plot_styles() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + + # CI-konforme Farbskala zwischen depthArea und brightArea + def _two_color_scale(c1, c2): + def _hex_to_rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + r1, g1, b1 = _hex_to_rgb(c1) + r2, g2, b2 = _hex_to_rgb(c2) + scale = [] + for t in np.linspace(0, 1, 6): + r = int(r1*(1-t) + r2*t) + g = int(g1*(1-t) + g2*t) + b = int(b1*(1-t) + b2*t) + scale.append([float(t), f"rgb({r},{g},{b})"]) + return scale + + colorscale = _two_color_scale(colors["depthArea"], colors["brightArea"]) if "depthArea" in colors else "Viridis" + + # Histogram2d + fig = go.Figure(data=go.Histogram2d( + x=df["Kapitel"].astype(int), + y=df["Effektstärke"], + nbinsx=max(1, df["Kapitel"].nunique()), + nbinsy=bins_d, + colorscale=colorscale, + colorbar=dict(title="Anzahl"), + hovertemplate="Kapitel: %{x}
d-Bin: %{y}
Anzahl: %{z}", + )) + + fig.update_layout(plotly_template.get_standard_layout( + f"Heatmap: Kapitel × Effektstärke ({kapitel_label})", "Kapitel", "Cohen d" + )) + # ganze Zahlen als Kapitel-Ticks + fig.update_layout(xaxis=dict(tickmode="linear", dtick=1)) + fig.show() + export_figure(fig, "vl-heatmap-kapitel-vs-d", export_fig_visual, export_fig_png) + +def export_json(obj: dict, name: str): + try: + p = os.path.join(EXPORT_DIR, name) + with open(p, "w", encoding="utf-8") as f: + json.dump(obj, f, ensure_ascii=False, indent=2) + except Exception: + pass + + +# ----------------------------------------- +# Hilfsfunktion: DataFrame zu Records (mit None für NaN) +# ----------------------------------------- +def _df_records(df: pd.DataFrame, cols: list[str]) -> list[dict]: + try: + return df[cols].replace({np.nan: None}).to_dict(orient="records") + except Exception: + return df.replace({np.nan: None}).to_dict(orient="records") + +def plot_boxplots(df: pd.DataFrame, kapitel: int | None = None): + """Boxplots der Effektstärken: nach Kapitel (falls Gesamt) und nach Bins. + - Zeigt Ausreißer (IQR-basiert), Notches für Median-Konfidenz. + - CI-Stile aus plotly_template. + """ + styles = plotly_template.get_plot_styles() + colors = plotly_template.get_colors() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + + # 1) Boxplot nach Kapitel (nur sinnvoll, wenn mehrere Kapitel vorhanden) + if kapitel is None and df["Kapitel"].nunique() > 1: + fig_kap = go.Figure() + fig_kap.add_trace(go.Box( + x=df["Kapitel"].astype(int), + y=df["Effektstärke"], + boxpoints="outliers", + notched=True, + marker=styles["marker_brightArea"], + line=styles["linie_primaryLine"], + name="Kapitel", + hovertemplate="Kapitel: %{x}
d: %{y:.2f}", + )) + fig_kap.update_layout(plotly_template.get_standard_layout( + f"Boxplot Effektstärken nach Kapitel ({kapitel_label})", "Kapitel", "Cohen d" + )) + # ganze Zahlen als Kapitel-Ticks + fig_kap.update_layout(xaxis=dict(tickmode="linear", dtick=1)) + fig_kap.show() + export_figure(fig_kap, "vl-box-kapitel", export_fig_visual, export_fig_png) + + # 2) Boxplot nach heuristischen Bins (immer möglich) + order = ["negativ", "gering", "mittel", "hoch"] + fig_bin = go.Figure() + fig_bin.add_trace(go.Box( + x=pd.Categorical(df["Bin"], categories=order, ordered=True), + y=df["Effektstärke"], + boxpoints="outliers", + notched=True, + marker=styles["marker_accent"], + line=styles["linie_secondaryLine"], + name="Bin", + hovertemplate="Bin: %{x}
d: %{y:.2f}", + )) + fig_bin.update_layout(plotly_template.get_standard_layout( + f"Boxplot Effektstärken nach Bins ({kapitel_label})", "Bin", "Cohen d" + )) + fig_bin.show() + export_figure(fig_bin, "vl-box-bins", export_fig_visual, export_fig_png) + + # 3) Optional: Ein Gesamt-Boxplot für die aktuelle Auswahl + fig_all = go.Figure() + fig_all.add_trace(go.Box( + y=df["Effektstärke"], + boxpoints="outliers", + notched=True, + marker=styles["marker_positiveHighlight"], + line=styles["linie_primaryLine"], + name=kapitel_label, + hovertemplate="d: %{y:.2f}", + )) + fig_all.update_layout(plotly_template.get_standard_layout( + f"Boxplot Effektstärken ({kapitel_label})", "", "Cohen d" + )) + fig_all.show() + export_figure(fig_all, "vl-box-overall", export_fig_visual, export_fig_png) + +def plot_hist(df: pd.DataFrame, kapitel: int | None = None): + # Use CI styles from plotly_template + styles = plotly_template.get_plot_styles() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + fig = go.Figure() + fig.add_trace(go.Histogram( + x=df["Effektstärke"], + marker=styles["balken_accent"], + hovertemplate="Effektstärke: %{x:.2f}
Häufigkeit: %{y}" + )) + fig.update_layout(plotly_template.get_standard_layout( + f"Verteilung der Effektstärken ({kapitel_label})", "Cohen d", "Häufigkeit" + )) + fig.show() + export_figure(fig, "vl-hist-effekte", export_fig_visual, export_fig_png) + + +def plot_bins(df: pd.DataFrame, kapitel: int | None = None): + styles = plotly_template.get_plot_styles() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + order = ["negativ", "gering", "mittel", "hoch"] + counts = df["Bin"].value_counts().reindex(order).fillna(0).astype(int) + fig = go.Figure() + fig.add_trace(go.Bar( + x=counts.index, + y=counts.values, + marker=styles["balken_accent"], + hovertemplate="Kategorie: %{x}
Anzahl: %{y}" + )) + fig.update_layout(plotly_template.get_standard_layout( + f"Heuristische Einteilung nach Effektstärke (Bins) ({kapitel_label})", "Bin", "Anzahl" + )) + fig.show() + export_figure(fig, "vl-bins", export_fig_visual, export_fig_png) + + +def plot_scatter(df: pd.DataFrame, cluster_labels: np.ndarray, model: KMeans, sil: float, title_suffix: str, kapitel: int | None = None): + styles = plotly_template.get_plot_styles() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + tmp = df.copy() + tmp["Cluster"] = cluster_labels.astype(int) + + # Plot-X: Kapitel als ganze Zahlen; kleine Jitter-Verschiebung, damit Punkte nicht exakt übereinander liegen + rng = np.random.default_rng(42) + tmp["_kapitel_x"] = tmp["Kapitel"].astype(int) + (rng.random(len(tmp)) - 0.5) * 0.12 + + # Clusterstärken (Mittelwert der Effektstärke im jeweiligen Clusterzentrum) + cluster_strengths = {i: float(model.cluster_centers_[i][0]) for i in range(len(model.cluster_centers_))} + tmp["Clusterstärke"] = tmp["Cluster"].map(cluster_strengths) + + hovertemplate = ( + "Thermometer: %{customdata[2]}
" + "Stichwort: %{text}
" + "Effektstärke: %{y:.2f}
" + "Kapitel: %{customdata[0]}
" + "Clusterstärke: %{customdata[1]:.3f}" + ) + + fig = go.Figure() + clusters = sorted(tmp["Cluster"].unique()) + palette_keys = ["positiveHighlight", "negativeHighlight", "accent", "brightArea"] + + for idx, cluster in enumerate(clusters): + cluster_df = tmp[tmp["Cluster"] == cluster] + color_key = palette_keys[idx % len(palette_keys)] + fig.add_trace(go.Scatter( + x=cluster_df["_kapitel_x"], + y=cluster_df["Effektstärke"], + mode="markers", + marker={**styles[f"marker_{color_key}"], "size": 10}, + name=f"Cluster: {cluster_strengths[cluster]:.2f}", + text=cluster_df["Stichwort"], + customdata=np.stack([cluster_df["Kapitelname"], cluster_df["Clusterstärke"], cluster_df["Thermometer_ID"]], axis=-1), + hovertemplate=hovertemplate + )) + + fig.update_layout(plotly_template.get_standard_layout( + f"Effektstärke × Cluster ({title_suffix}) ({kapitel_label}) – Silhouette: {sil:.3f}", "Kapitel", "Cohen d" + )) + # Ganze Zahlen auf der x‑Achse (Kapitel) + fig.update_layout(xaxis=dict(tickmode="linear", dtick=1)) + fig.show() + export_figure(fig, f"vl-scatter-{title_suffix}", export_fig_visual, export_fig_png) + + +def plot_scatter_3d(df: pd.DataFrame, cluster_labels: np.ndarray, sil: float, title_suffix: str, kapitel: int | None = None): + styles = plotly_template.get_plot_styles() + kapitel_label = f"Kapitel {kapitel}" if kapitel else "Gesamt" + tmp = df.copy() + tmp["Cluster"] = cluster_labels.astype(int) + + # Clusterzentren für durchschnittliche Effektstärke + cluster_strengths = {i: float(tmp[tmp["Cluster"] == i]["Effektstärke"].mean()) for i in sorted(set(cluster_labels))} + + hovertemplate = ( + "Thermometer: %{text}
" + "Kapitel: %{customdata[0]}
" + "Effektstärke: %{x:.2f}
" + "Textdim: %{z:.2f}
" + "Cluster: %{customdata[1]}" + ) + + fig = go.Figure() + clusters = sorted(tmp["Cluster"].unique()) + palette_keys = ["positiveHighlight", "negativeHighlight", "accent", "brightArea"] + + for idx, cluster in enumerate(clusters): + cluster_df = tmp[tmp["Cluster"] == cluster] + color_key = palette_keys[idx % len(palette_keys)] + fig.add_trace(go.Scatter3d( + x=cluster_df["Effektstärke"], + y=cluster_df["Kapitel"], + z=cluster_df["Text_Dimension"], + mode="markers", + marker={**styles[f"marker_{color_key}"], "size": 6}, + name=f"Cluster {cluster} (Ø d = {cluster_strengths[cluster]:.2f})", + text=cluster_df["Stichwort"], + customdata=np.stack([cluster_df["Kapitelname"], cluster_df["Cluster"]], axis=-1), + hovertemplate=hovertemplate + )) + + fig.update_layout(plotly_template.get_standard_layout( + f"3D-Clustering (Effektstärke × Kapitel × Text) – {title_suffix} ({kapitel_label}) – Silhouette: {sil:.3f}", + "Effektstärke", "Kapitel", "Textdimension" + )) + fig.update_layout(scene=dict( + yaxis=dict( + title="Kapitel", + tickmode="linear", + dtick=1 + ) + )) + fig.show() + export_figure(fig, f"vl-scatter3d-{title_suffix}", export_fig_visual, export_fig_png) + +# ----------------------------------------- +# Pipeline +# ----------------------------------------- + +def analyse(csv_path: str = "Thermometer.csv", k: int = 4, kapitel: int | None = None): + # Laden + df = load_data(csv_path) + + # Datenvalidierung + dq = validate_data(df) + print("\nDATA QUALITY REPORT:") + for key, val in dq.items(): + print(f" {key}: {val}") + export_json(dq, "data_quality_report.json") + if kapitel is not None: + df = df[df["Kapitel"] == kapitel] + if df.empty: + print(f"Keine Daten für Kapitel {kapitel}.") + return None + + # Bins + df = add_manual_bins(df) + + # K-Means + labels, sil, model = run_kmeans(df, k=k) + # Silhouette je Punkt anhängen + try: + X_for_sil, _ = encode_features(df) + if k > 1 and len(df) > k: + df["Silhouette_point"] = silhouette_samples(X_for_sil, labels) + else: + df["Silhouette_point"] = np.nan + except Exception: + df["Silhouette_point"] = np.nan + + # Reports + print("—" * 60) + print("ANALYSE Sicht 1 | Heuristische Bins (Grenzen: <0 | <0.40 | <0.70 | ≥0.70)") + print(df["Bin"].value_counts().reindex(["negativ", "gering", "mittel", "hoch"]).fillna(0).astype(int)) + + print("\nANALYSE Sicht 2 | K-Means-Clustering") + if not math.isnan(sil): + print(f"Silhouette-Score (k={k}): {sil:.3f}") + else: + print(f"Silhouette-Score (k={k}): n/a (zu wenige Daten oder k zu groß)") + + if not math.isnan(sil): + # Clusterzentren (nur Effektstärke + Kapitelmittelwerte) + centers = model.cluster_centers_ + print("\nClusterzentren (erste Spalte = Effektstärke, Rest = Kapitel-OHE):") + for idx, center in enumerate(centers): + eff = center[0] + print(f" Cluster {idx}: Effektstärke-Mittel {eff:.3f}") + + # --- Statistik-Block --- + stats_df = describe_effects(df) + plot_table_stats(stats_df, f"Deskriptive Statistik ({'Kapitel '+str(kapitel) if kapitel else 'Gesamt'})") + # Deskriptive Statistik exportieren + try: + stats_df.to_csv(os.path.join(EXPORT_DIR, "deskriptiv.csv")) + except Exception: + pass + normality_and_qq(df, kapitel=kapitel) + df = mark_outliers_iqr(df) + if kapitel is None: + group_tests_by_kapitel(df) + text_vs_effect(df) + if kapitel is None: + chi2_bins_kapitel(df) + cluster_diagnostics(df) + profiles_df = cluster_profiles(df, labels) + try: + export_json(json.loads(profiles_df.to_json(orient="table")), "cluster_profile.json") + except Exception: + pass + + # --- Signifikanz-geführte Sicht --- + df_sig = build_significance_view(df) + try: + df_sig.sort_values("Rank_score").to_csv(os.path.join(EXPORT_DIR, "signifikanz_ranking.csv"), index=False) + except Exception: + pass + plot_significance_space(df_sig) + + # Tests zusammenfassen (vor Export sammeln) + tests_summary = {"silhouette_global": float(sil) if not math.isnan(sil) else None} + + if kapitel is None and df["Kapitel"].nunique() > 1: + groups = [g.dropna().values for _, g in df.groupby("Kapitel")["Effektstärke"]] + try: + lev = levene(*groups, center='median') + tests_summary["levene_W"] = float(lev.statistic) + tests_summary["levene_p"] = float(lev.pvalue) + except Exception: + pass + try: + kw = kruskal(*groups) + n_total = sum(len(g) for g in groups) + h = float(kw.statistic) + eps2 = (h - (len(groups)-1)) / (n_total - 1) if n_total > 1 else None + tests_summary["kruskal_H"] = h + tests_summary["kruskal_p"] = float(kw.pvalue) + tests_summary["kruskal_eps2"] = float(eps2) if eps2 is not None else None + except Exception: + pass + + try: + if "Text_Dimension" not in df.columns: + encode_features_3d(df) + rho, p = spearmanr(df["Text_Dimension"], df["Effektstärke"], nan_policy='omit') + tests_summary["spearman_rho_text_d"] = float(rho) + tests_summary["spearman_p_text_d"] = float(p) + except Exception: + pass + + if kapitel is None and df["Kapitel"].nunique() > 1: + try: + ct = pd.crosstab(df["Kapitel"], df["Bin"]).reindex(sorted(df["Kapitel"].unique())) + chi2 = stats.chi2_contingency(ct) + tests_summary["chi2"] = float(chi2[0]) + tests_summary["chi2_p"] = float(chi2[1]) + tests_summary["chi2_df"] = int(chi2[2]) + except Exception: + pass + + export_json(tests_summary, "tests_summary.json") + + # --- WERTEDATEI: Alles in einer JSON bündeln --- + if export_werte_all: + try: + # Kern-Datenzeilen + base_cols = [ + "Thermometer_ID", "Stichwort", "Effektstärke", "Kapitel", "Kapitelname", + "Bin", "Text_Dimension", "Outlier_IQR", "Silhouette_point" + ] + rows = _df_records(df, [c for c in base_cols if c in df.columns]) + + # Deskriptive Statistik als Records + desc_records = _df_records( + (stats_df.reset_index().rename(columns={"index": "Gruppe"})), + list(stats_df.reset_index().columns) + ) + + # Cluster-Profile als Records + prof_records = _df_records( + (profiles_df.reset_index().rename(columns={"index": "Cluster"})), + list(profiles_df.reset_index().columns) + ) + + # Cluster-Zentren (voll) und nur d-Komponente + centers_full = model.cluster_centers_.tolist() if hasattr(model, "cluster_centers_") else None + centers_d = [float(c[0]) for c in model.cluster_centers_] if hasattr(model, "cluster_centers_") else None + + payload = { + "meta": { + "k": int(k), + "kapitel": int(kapitel) if kapitel is not None else None, + "theme": theme, + }, + "data": rows, + "deskriptiv": desc_records, + "cluster": { + "silhouette_global": float(sil) if not math.isnan(sil) else None, + "centers_full": centers_full, + "centers_effekt_only": centers_d, + }, + "profiles": prof_records, + "tests_summary": tests_summary if isinstance(tests_summary, dict) else {}, + "data_quality": dq, + } + export_json(payload, "werte_all.json") + except Exception as _e: + pass + + # Plots + plot_heatmap_kapitel_vs_d(df, kapitel=kapitel) + # Boxplots + plot_boxplots(df, kapitel=kapitel) + plot_hist(df, kapitel=kapitel) + plot_bins(df, kapitel=kapitel) + plot_scatter(df, labels, model, sil, title_suffix=f"k{k}", kapitel=kapitel) + + # 3D-Clustering + X3d, _ = encode_features_3d(df) + model3d = KMeans(n_clusters=k, n_init=20, random_state=42) + labels3d = model3d.fit_predict(X3d) + sil3d = silhouette_score(X3d, labels3d) if k > 1 and len(df) > k else np.nan + plot_scatter_3d(df, labels3d, sil3d, title_suffix=f"k{k}-3d", kapitel=kapitel) + + # Clusterzuordnung exportieren + try: + df_export = df.copy() + df_export["Cluster"] = labels + df_export.to_csv(os.path.join(EXPORT_DIR, "clusterzuordnung.csv"), index=False) + except Exception: + pass + + return { + "df": df, + "kmeans_labels": labels, + "silhouette": sil, + "model": model, + } + + +# ----------------------------------------- +# Main +# ----------------------------------------- +if __name__ == "__main__": + # Passe den CSV-Pfad an, falls die Datei woanders liegt + if analyse_all: + df_all = load_data(os.path.join(os.path.dirname(__file__), csv_file)) + for kap in sorted(df_all["Kapitel"].unique()): + analyse(csv_path=os.path.join(os.path.dirname(__file__), csv_file), k=k_clusters, kapitel=kap) + else: + analyse(csv_path=os.path.join(os.path.dirname(__file__), csv_file), k=k_clusters, kapitel=selected_kapitel)