Compare commits
3 Commits
b56166a780
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e1360d49b | |||
| d3f0d865a4 | |||
| ef140a5df1 |
@ -13,6 +13,7 @@ Nachgewiesen werden:
|
|||||||
## Inhalt
|
## Inhalt
|
||||||
|
|
||||||
- `lms_statistische-analyse.py`: Hauptskript zur statistischen Auswertung
|
- `lms_statistische-analyse.py`: Hauptskript zur statistischen Auswertung
|
||||||
|
- `dendrogramm_cluster_anteile_matplotlib.png`: automatisch erzeugte Dendrogramm-Visualisierung auf Basis der Kursanteile (Ward-Linkage, matplotlib)
|
||||||
- `lms-verteilung.xlsx`: Zuordnung von Handlungssituationen zu APrV-Kürzeln
|
- `lms-verteilung.xlsx`: Zuordnung von Handlungssituationen zu APrV-Kürzeln
|
||||||
- `APrV-Kuerzel_zu_Kompetenzbereichen.csv`: Mapping von Kürzeln zu Kompetenzbereichen
|
- `APrV-Kuerzel_zu_Kompetenzbereichen.csv`: Mapping von Kürzeln zu Kompetenzbereichen
|
||||||
- Visualisierungen werden automatisiert erzeugt; exportierte PNG-Dateien sind optional und werden lokal gespeichert
|
- Visualisierungen werden automatisiert erzeugt; exportierte PNG-Dateien sind optional und werden lokal gespeichert
|
||||||
@ -22,6 +23,7 @@ Nachgewiesen werden:
|
|||||||
- Automatisierte Zuordnung über Pandas
|
- Automatisierte Zuordnung über Pandas
|
||||||
- Visualisierung mit Plotly (Boxplots, Tortendiagramme, Balkendiagramme)
|
- Visualisierung mit Plotly (Boxplots, Tortendiagramme, Balkendiagramme)
|
||||||
- Vergleich mit APrV-Gesetzesvorgaben als Referenzstruktur
|
- Vergleich mit APrV-Gesetzesvorgaben als Referenzstruktur
|
||||||
|
- Hierarchische Clusteranalyse mit `scipy` + `matplotlib` zur Dendrogrammerstellung auf Basis standardisierter Kursanteile
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ Dieses Projekt steht unter der [Creative Commons Attribution 4.0 International L
|
|||||||
Für kommerzielle Nutzungen, die über die Bedingungen der CC BY 4.0 hinausgehen, bitte Kontakt an:
|
Für kommerzielle Nutzungen, die über die Bedingungen der CC BY 4.0 hinausgehen, bitte Kontakt an:
|
||||||
[kontakt@jochen-hanisch.de](mailto:kontakt@jochen-hanisch.de)
|
[kontakt@jochen-hanisch.de](mailto:kontakt@jochen-hanisch.de)
|
||||||
|
|
||||||
|
https://zenodo.org/records/15565960
|
||||||
|
|
||||||
## Hinweis
|
## Hinweis
|
||||||
|
|
||||||
Dieses Repository ist Bestandteil der Dissertationsarbeit an der Charité – Universitätsmedizin Berlin.
|
Dieses Repository ist Bestandteil der Dissertationsarbeit an der Charité – Universitätsmedizin Berlin.
|
||||||
@ -8,6 +8,9 @@ from ci_template.plotly_template import (
|
|||||||
get_standard_layout, get_colors, set_theme,
|
get_standard_layout, get_colors, set_theme,
|
||||||
plot_table_from_dict, plot_table_from_dataframe
|
plot_table_from_dict, plot_table_from_dataframe
|
||||||
)
|
)
|
||||||
|
from sklearn.cluster import KMeans
|
||||||
|
from sklearn.metrics import silhouette_score
|
||||||
|
from sklearn.preprocessing import StandardScaler
|
||||||
set_theme(plotly_theme)
|
set_theme(plotly_theme)
|
||||||
|
|
||||||
# --- Hilfsfunktionen für Export und Slugify ---
|
# --- Hilfsfunktionen für Export und Slugify ---
|
||||||
@ -364,6 +367,168 @@ plot_vergleich_lehrplan_aprv()
|
|||||||
plot_vergleich_kompetenz_aprv()
|
plot_vergleich_kompetenz_aprv()
|
||||||
plot_boxplots(df)
|
plot_boxplots(df)
|
||||||
|
|
||||||
|
|
||||||
|
# --- K-Means-Clusteranalyse auf Basis der Themenbereichsanteile ---
|
||||||
|
anteilsmerkmale = ['Anteil medizinisch', 'Anteil rettungsdienstlich', 'Anteil bezugswissenschaftlich']
|
||||||
|
|
||||||
|
if df[anteilsmerkmale].nunique().eq(1).all():
|
||||||
|
print("⚠️ Alle Anteilsmerkmale sind konstant. K-Means-Clustering wird übersprungen.")
|
||||||
|
df['Cluster_Anteile'] = 'Nicht gültig'
|
||||||
|
silhouette_avg_anteile = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
scaler = StandardScaler()
|
||||||
|
scaled_anteile = scaler.fit_transform(df[anteilsmerkmale])
|
||||||
|
|
||||||
|
if len(df) < 2:
|
||||||
|
raise ValueError("Zu wenige Datenpunkte für die Clusteranalyse der Anteile.")
|
||||||
|
|
||||||
|
kmeans_anteile = KMeans(n_clusters=4, random_state=42)
|
||||||
|
df['Cluster_Anteile'] = kmeans_anteile.fit_predict(scaled_anteile).astype(str)
|
||||||
|
|
||||||
|
silhouette_avg_anteile = silhouette_score(scaled_anteile, df['Cluster_Anteile'].astype(int))
|
||||||
|
print(f"Silhouette-Score (Anteile): {silhouette_avg_anteile:.4f}")
|
||||||
|
|
||||||
|
# Beschreibung der Cluster
|
||||||
|
cluster_means_anteile = df[anteilsmerkmale + ['Cluster_Anteile']].groupby('Cluster_Anteile').mean()
|
||||||
|
cluster_labels_anteile = {
|
||||||
|
str(c): "<br>".join(cluster_means_anteile.loc[str(c)].sort_values(ascending=False).head(3).index)
|
||||||
|
for c in cluster_means_anteile.index
|
||||||
|
}
|
||||||
|
df['Cluster_Label_Anteile'] = df['Cluster_Anteile'].map(cluster_labels_anteile)
|
||||||
|
|
||||||
|
for cluster, label in cluster_labels_anteile.items():
|
||||||
|
print(f"Cluster {cluster}: {label.replace('<br>', ', ')}")
|
||||||
|
|
||||||
|
# Visualisierung als 3D-Scatterplot
|
||||||
|
import plotly.express as px
|
||||||
|
colors = get_colors()
|
||||||
|
fig_anteile = px.scatter_3d(
|
||||||
|
df,
|
||||||
|
x='Anteil medizinisch',
|
||||||
|
y='Anteil rettungsdienstlich',
|
||||||
|
z='Anteil bezugswissenschaftlich',
|
||||||
|
color='Cluster_Label_Anteile',
|
||||||
|
size_max=50,
|
||||||
|
color_discrete_sequence=list(colors.values()),
|
||||||
|
hover_data=anteilsmerkmale + ['Cluster_Label_Anteile'],
|
||||||
|
title=f"Clusteranalyse (Themenanteile) | Silhouette: {silhouette_avg_anteile:.4f}",
|
||||||
|
labels={
|
||||||
|
'Anteil medizinisch': 'Medizinisch',
|
||||||
|
'Anteil rettungsdienstlich': 'Rettungsdienstlich',
|
||||||
|
'Anteil bezugswissenschaftlich': 'Bezugswissenschaftlich',
|
||||||
|
'Cluster_Label_Anteile': 'Cluster-Beschreibung'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
fig_anteile.update_layout(get_standard_layout(
|
||||||
|
title="K-Means-Clusteranalyse nach Themenanteilen",
|
||||||
|
x_title='Medizinisch',
|
||||||
|
y_title='Rettungsdienstlich',
|
||||||
|
z_title='Bezugswissenschaftlich'
|
||||||
|
))
|
||||||
|
export_figure(fig_anteile, "kmeans_cluster_anteile", export_fig_visual, export_fig_png)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler bei der Clusteranalyse auf Anteilsdaten: {e}")
|
||||||
|
df['Cluster_Anteile'] = 'Fehler'
|
||||||
|
silhouette_avg_anteile = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- Hierarchisches Clustering der Themenanteile mit Dendrogramm ---
|
||||||
|
|
||||||
|
|
||||||
|
# --- Interaktives Dendrogramm mit Plotly ---
|
||||||
|
|
||||||
|
from scipy.cluster.hierarchy import dendrogram, linkage
|
||||||
|
import plotly.graph_objects as go
|
||||||
|
|
||||||
|
# Berechnung der Linkage-Matrix für hierarchisches Clustering
|
||||||
|
try:
|
||||||
|
linkage_matrix = linkage(scaled_anteile, method='ward')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Erzeugen der Linkage-Matrix für das Dendrogramm: {e}")
|
||||||
|
linkage_matrix = None
|
||||||
|
|
||||||
|
def create_plotly_dendrogram(linkage_matrix, labels, orientation='bottom'):
|
||||||
|
colors = get_colors()
|
||||||
|
dendro = dendrogram(linkage_matrix, labels=labels, orientation=orientation, no_plot=True)
|
||||||
|
icoord = dendro['icoord']
|
||||||
|
dcoord = dendro['dcoord']
|
||||||
|
color_list = dendro['color_list']
|
||||||
|
label = dendro['ivl']
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for i in range(len(icoord)):
|
||||||
|
xs = icoord[i]
|
||||||
|
ys = dcoord[i]
|
||||||
|
data.append(go.Scatter(
|
||||||
|
x=xs,
|
||||||
|
y=ys,
|
||||||
|
mode='lines',
|
||||||
|
line=dict(color=colors["primaryLine"], width=2),
|
||||||
|
hoverinfo='none'
|
||||||
|
))
|
||||||
|
|
||||||
|
layout = get_standard_layout(
|
||||||
|
title="Interaktives Dendrogramm der Kursanteile (Ward-Linkage)",
|
||||||
|
x_title="Kursbezeichnung",
|
||||||
|
y_title="Distanz"
|
||||||
|
)
|
||||||
|
layout.update(dict(
|
||||||
|
xaxis=dict(tickvals=list(range(5, len(labels)*10+5, 10)), ticktext=label, tickangle=90),
|
||||||
|
showlegend=False,
|
||||||
|
margin=dict(l=40, r=0, b=120, t=50),
|
||||||
|
width=1200,
|
||||||
|
height=600
|
||||||
|
))
|
||||||
|
|
||||||
|
fig_dendrogramm_anteile = go.Figure(data=data, layout=layout)
|
||||||
|
export_figure(fig_dendrogramm_anteile, "dendrogramm_cluster_anteile", export_fig_visual, export_fig_png)
|
||||||
|
fig_dendrogramm_anteile.show()
|
||||||
|
|
||||||
|
|
||||||
|
# linkage_matrix und scaled_anteile werden weiter oben erzeugt
|
||||||
|
create_plotly_dendrogram(linkage_matrix, df["Kursbezeichnung"].tolist())
|
||||||
|
|
||||||
|
|
||||||
# Tabellen-Visualisierungen nach bisherigen Plots aus ci_template aufrufen
|
# Tabellen-Visualisierungen nach bisherigen Plots aus ci_template aufrufen
|
||||||
plot_table_from_dict(gesamt_stats, "Gesamtstatistik der curricularen Struktur", export_figure, export_fig_visual, export_fig_png)
|
plot_table_from_dict(gesamt_stats, "Gesamtstatistik der curricularen Struktur", export_figure, export_fig_visual, export_fig_png)
|
||||||
plot_table_from_dataframe(gruppe, "Themenbereich", "Gruppierte Statistik nach Themenbereichen", export_figure, export_fig_visual, export_fig_png)
|
plot_table_from_dataframe(gruppe, "Themenbereich", "Gruppierte Statistik nach Themenbereichen", export_figure, export_fig_visual, export_fig_png)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Matplotlib-Dendrogramm-Plot und Export (CI-konform) ---
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from scipy.cluster.hierarchy import dendrogram, linkage
|
||||||
|
|
||||||
|
def plot_dendrogram_matplotlib(df, scaled_anteile):
|
||||||
|
try:
|
||||||
|
linkage_matrix = linkage(scaled_anteile, method='ward')
|
||||||
|
fig, ax = plt.subplots(figsize=(16, 8))
|
||||||
|
dendrogram(
|
||||||
|
linkage_matrix,
|
||||||
|
labels=df["Kursbezeichnung"].tolist(),
|
||||||
|
leaf_rotation=90,
|
||||||
|
leaf_font_size=10,
|
||||||
|
color_threshold=None
|
||||||
|
)
|
||||||
|
ax.set_title("Dendrogramm der Kursanteile (Ward-Linkage)", fontsize=14)
|
||||||
|
ax.set_xlabel("Kursbezeichnung", fontsize=12)
|
||||||
|
ax.set_ylabel("Distanz", fontsize=12)
|
||||||
|
fig.tight_layout()
|
||||||
|
|
||||||
|
# Speichern
|
||||||
|
export_path_png = os.path.join(
|
||||||
|
"..", "Allgemein beruflich", "Research", "Forschungsprojekte",
|
||||||
|
"Systemische Kompetenzentwicklung für High Responsibility Teams",
|
||||||
|
"dendrogramm_cluster_anteile_matplotlib.png"
|
||||||
|
)
|
||||||
|
fig.savefig(export_path_png, dpi=300)
|
||||||
|
print(f"✅ Matplotlib-Dendrogramm gespeichert: {export_path_png}")
|
||||||
|
plt.close(fig)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Fehler beim Erzeugen des Matplotlib-Dendrogramms: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# Am Ende aufrufen
|
||||||
|
plot_dendrogram_matplotlib(df, scaled_anteile)
|
||||||
|
|||||||
Reference in New Issue
Block a user