Tobi-ewl commited on
Commit
83376ef
·
verified ·
1 Parent(s): 56f602e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +300 -112
app.py CHANGED
@@ -101,6 +101,11 @@ if "szenario_bestaetigt" not in st.session_state:
101
  if "szenario" not in st.session_state:
102
  st.session_state.szenario = None
103
 
 
 
 
 
 
104
  szenario_df = load_szenario_data('Data/Szenario-Input.csv')
105
  szenario_liste = list(szenario_df.index)
106
 
@@ -521,9 +526,9 @@ elif modus == "Upload csv-Datei":
521
  else:
522
  st.session_state.batch_user_values = None
523
 
524
- annuitaeten_gesamt = []
525
- annuitaeten_objekt_map = {}
526
-
527
  do_calc = st.button("Batch-Berechnung starten")
528
  if do_calc:
529
  try:
@@ -535,6 +540,9 @@ elif modus == "Upload csv-Datei":
535
  df_out[name] = ""
536
  df_out[hname] = ""
537
 
 
 
 
538
  for idx, row in df_input.iterrows():
539
  try:
540
  nutzflaeche = row["Wohnflaeche"]
@@ -556,18 +564,16 @@ elif modus == "Upload csv-Datei":
556
  "Fixkosten_O+M", "Preisänderungsfaktor_O+M", "Emissionsänderungsfaktor", "Förderung"]:
557
  df[col] = df[col].astype(str).str.replace(",", ".").str.replace("|", ".")
558
  df[col] = pd.to_numeric(df[col], errors='coerce')
559
- # Wenn Batch-global-Werte gesetzt sind, anwenden:
560
  batch_user_vals = st.session_state.get("batch_user_values")
561
  if batch_user_vals is not None:
562
- # Mapping via Name zur Sicherheit
563
  preview_heizsys_names = {preview_df.loc[x, "Name"]: x for x in preview_df.index}
564
  for pidx in batch_user_vals:
565
  sysname = preview_df.loc[pidx, "Name"]
566
- # Suche nach dem Namen im aktuellen DF
567
  df_idx = df[df["Name"] == sysname].index
568
  if not df_idx.empty:
569
  for key in batch_user_vals[pidx]:
570
  df.at[df_idx[0], key] = batch_user_vals[pidx][key]
 
571
  energiebedarf = energiebedarf_spezifisch
572
  df["Energiebedarf"] = df.apply(lambda rowh: calculate_energiebedarf(
573
  energiebedarf, nutzflaeche, rowh["Effizienz"]), axis=1)
@@ -586,133 +592,315 @@ elif modus == "Upload csv-Datei":
586
  df["Annuität_NS"] = 0
587
  df["Annuität"] = df["Annuität_NK"] + df["Annuität_NV"] + df["Annuität_NB"] + df["Annuität_NS"]
588
  df = df.sort_values("Annuität")
 
589
  df_anni = df[[
590
  "Name", "Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"
591
  ]].copy()
592
  df_anni["Objekt-ID"] = row.get("Objekt-ID", idx)
593
  annuitaeten_gesamt.append(df_anni)
594
  annuitaeten_objekt_map[str(row.get("Objekt-ID", idx))] = df_anni
595
-
596
  for j in range(min(len(df), max_count)):
597
  df_out.at[idx, annuität_colnames[j]] = int(df.iloc[j]["Annuität"])
598
  df_out.at[idx, hsystem_colnames[j]] = df.iloc[j]["Name"]
599
  except Exception as e:
600
  st.warning(f"Objekt-ID {row.get('Objekt-ID', idx)}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
 
602
- heizsystem_namensmap = {
603
- "Ölheizung": "Oelheizung",
604
- "Luft-Wasser Wärmepumpe": "Luft-Wasser Waermepumpe",
605
- "Sole-Wasser Wärmepumpe": "Sole-Wasser Waermepumpe",
606
- "Wasser-Wasser Wärmepumpe": "Wasser-Wasser Waermepumpe",
607
- }
608
- for col in hsystem_colnames:
609
- df_out[col] = df_out[col].replace(heizsystem_namensmap)
610
 
611
- st.success("Berechnung abgeschlossen!")
612
 
613
- if annuitaeten_gesamt:
614
- # Verbinde alle Einzel-DFs zu einem großen Table
615
- df_alle = pd.concat(annuitaeten_gesamt, ignore_index=True)
616
- # Mittelwerte pro System
617
- df_mittel = df_alle.groupby("Name")[["Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"]].mean(numeric_only=True).reset_index()
618
 
619
- # Für gestapeltes Balkendiagramm (wie im Einzel-Modus)
620
- df_stacked = df_mittel.melt(
621
- id_vars="Name",
622
- value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
623
- var_name="Kostenart",
624
- value_name="Wert"
625
- )
626
- df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
627
- "Annuität_NK": "Kapitalgebundene Kosten",
628
- "Annuität_NV": "Bedarfsgebundene Kosten",
629
- "Annuität_NB": "Betriebsgebundene Kosten"
630
- })
631
- kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
632
- df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
633
- color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
634
 
635
- # Heizsysteme nach mittlerer Gesamtkosten sortieren
636
- sortierte_names = df_mittel.sort_values("Annuität")["Name"]
637
 
638
- st.markdown("### Mittlere annualisierte Kosten pro Heizsystem (Batch-Durchschnitt)")
639
- st.altair_chart(
640
- alt.Chart(df_stacked)
641
- .mark_bar()
642
- .encode(
643
- x=alt.X("Wert:Q", title="mittlere annualisierte Kosten (€)", stack="zero"),
644
- y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
645
- color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
646
- tooltip=["Name", "Kostenart", "Wert"]
647
- ).properties(height=350, width=650),
648
- use_container_width=True
649
- )
650
 
651
- csv_buffer = io.StringIO()
652
- df_out.to_csv(csv_buffer, sep=";", index=False)
653
- csv_bytes = csv_buffer.getvalue().encode("utf-8")
654
- st.download_button(
655
- "Download Ergebnis-CSV",
656
- csv_bytes,
657
- file_name="heizsysteme_batch_ergebnis.csv",
658
- mime="text/csv"
659
- )
660
 
661
- # --- Einzelobjekt-Grafik nach dem Batch-Durchschnitt (immer NACH Mittelwert und Download-Button!) ---
662
 
663
- if annuitaeten_objekt_map and len(annuitaeten_objekt_map) > 0:
664
- st.markdown("### Einzelanalyse eines Objekts (wie Einzel-Darstellung)")
665
 
666
- objekt_ids = list(annuitaeten_objekt_map.keys())
667
- # EIGENER Key, EIGENE Session-Variable nur für diese Auswahl!
668
- col1, col2 = st.columns([5, 2])
669
- with col1:
670
- auswahl = st.selectbox(
671
- "Objekt für Detaildarstellung auswählen", objekt_ids, key="batch_detail_sbox",
672
- index=objekt_ids.index(st.session_state["selected_objekt_id_batch_detail"])
673
- if st.session_state["selected_objekt_id_batch_detail"] in objekt_ids else 0
674
- )
675
- with col2:
676
- if st.button("Kostenvergleich für ausgewähltes Objekt anzeigen", key="batch_detail_show_btn"):
677
- st.session_state["selected_objekt_id_batch_detail"] = auswahl
678
 
679
- # Nach dem Buttonklick bleibt das Diagramm sichtbar, bis ein neuer Klick gemacht wird
680
- show_id = st.session_state["selected_objekt_id_batch_detail"]
681
- if show_id and show_id in annuitaeten_objekt_map:
682
- df_einzel = annuitaeten_objekt_map[show_id]
683
- df_stacked = df_einzel[["Name", "Annuität_NK", "Annuität_NV", "Annuität_NB"]].melt(
684
- id_vars="Name",
685
- value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
686
- var_name="Kostenart",
687
- value_name="Wert"
688
- )
689
- df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
690
- "Annuität_NK": "Kapitalgebundene Kosten",
691
- "Annuität_NV": "Bedarfsgebundene Kosten",
692
- "Annuität_NB": "Betriebsgebundene Kosten"
693
- })
694
- kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
695
- df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
696
- color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
697
- sortierte_names = df_einzel.sort_values("Annuität")["Name"]
698
 
699
- st.markdown(f"#### Annualisierte Kosten für Objekt {show_id}")
700
- st.altair_chart(
701
- alt.Chart(df_stacked)
702
- .mark_bar()
703
- .encode(
704
- x=alt.X("Wert:Q", title="Annualisierte Kosten (€)", stack="zero"),
705
- y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
706
- color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
707
- tooltip=["Name", "Kostenart", "Wert"]
708
- ).properties(height=350, width=650),
709
- use_container_width=True
710
- )
711
-
712
- except Exception as e:
713
- st.error(f"Fehler beim Einlesen/Berechnen: {e}")
714
 
715
- st.markdown("---")
716
- st.caption("Berechnung nach VDI 2067, Heizsystemauswahl gemäß DIN EN 15378.")
717
- else:
718
- st.warning("Bitte laden Sie eine CSV-Datei hoch", icon="⚠️")
 
101
  if "szenario" not in st.session_state:
102
  st.session_state.szenario = None
103
 
104
+ if "annuitaeten_objekt_map_batch" not in st.session_state:
105
+ st.session_state["annuitaeten_objekt_map_batch"] = {}
106
+ if "annuitaeten_gesamt_batch" not in st.session_state:
107
+ st.session_state["annuitaeten_gesamt_batch"] = []
108
+
109
  szenario_df = load_szenario_data('Data/Szenario-Input.csv')
110
  szenario_liste = list(szenario_df.index)
111
 
 
526
  else:
527
  st.session_state.batch_user_values = None
528
 
529
+ st.session_state["annuitaeten_gesamt_batch"] = annuitaeten_gesamt
530
+ st.session_state["annuitaeten_objekt_map_batch"] = annuitaeten_objekt_map
531
+
532
  do_calc = st.button("Batch-Berechnung starten")
533
  if do_calc:
534
  try:
 
540
  df_out[name] = ""
541
  df_out[hname] = ""
542
 
543
+ annuitaeten_gesamt = []
544
+ annuitaeten_objekt_map = {}
545
+
546
  for idx, row in df_input.iterrows():
547
  try:
548
  nutzflaeche = row["Wohnflaeche"]
 
564
  "Fixkosten_O+M", "Preisänderungsfaktor_O+M", "Emissionsänderungsfaktor", "Förderung"]:
565
  df[col] = df[col].astype(str).str.replace(",", ".").str.replace("|", ".")
566
  df[col] = pd.to_numeric(df[col], errors='coerce')
 
567
  batch_user_vals = st.session_state.get("batch_user_values")
568
  if batch_user_vals is not None:
 
569
  preview_heizsys_names = {preview_df.loc[x, "Name"]: x for x in preview_df.index}
570
  for pidx in batch_user_vals:
571
  sysname = preview_df.loc[pidx, "Name"]
 
572
  df_idx = df[df["Name"] == sysname].index
573
  if not df_idx.empty:
574
  for key in batch_user_vals[pidx]:
575
  df.at[df_idx[0], key] = batch_user_vals[pidx][key]
576
+
577
  energiebedarf = energiebedarf_spezifisch
578
  df["Energiebedarf"] = df.apply(lambda rowh: calculate_energiebedarf(
579
  energiebedarf, nutzflaeche, rowh["Effizienz"]), axis=1)
 
592
  df["Annuität_NS"] = 0
593
  df["Annuität"] = df["Annuität_NK"] + df["Annuität_NV"] + df["Annuität_NB"] + df["Annuität_NS"]
594
  df = df.sort_values("Annuität")
595
+
596
  df_anni = df[[
597
  "Name", "Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"
598
  ]].copy()
599
  df_anni["Objekt-ID"] = row.get("Objekt-ID", idx)
600
  annuitaeten_gesamt.append(df_anni)
601
  annuitaeten_objekt_map[str(row.get("Objekt-ID", idx))] = df_anni
602
+
603
  for j in range(min(len(df), max_count)):
604
  df_out.at[idx, annuität_colnames[j]] = int(df.iloc[j]["Annuität"])
605
  df_out.at[idx, hsystem_colnames[j]] = df.iloc[j]["Name"]
606
  except Exception as e:
607
  st.warning(f"Objekt-ID {row.get('Objekt-ID', idx)}: {e}")
608
+
609
+ # Im Session-State speichern!
610
+ st.session_state["annuitaeten_gesamt_batch"] = annuitaeten_gesamt
611
+ st.session_state["annuitaeten_objekt_map_batch"] = annuitaeten_objekt_map
612
+
613
+ # (Optional: Platz-1-Übersicht, Mapping, weitere Batch-Funktionen ...)
614
+ st.success("Berechnung abgeschlossen!")
615
+ except Exception as e:
616
+ st.error(f"Fehler beim Einlesen/Berechnen: {e}")
617
+
618
+ # --- Batch-Mittelwert-Grafik/Nachbereitung ---
619
+
620
+ if st.session_state["annuitaeten_gesamt_batch"]:
621
+ df_alle = pd.concat(st.session_state["annuitaeten_gesamt_batch"], ignore_index=True)
622
+ df_mittel = df_alle.groupby("Name")[["Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"]].mean(numeric_only=True).reset_index()
623
+ df_stacked = df_mittel.melt(
624
+ id_vars="Name",
625
+ value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
626
+ var_name="Kostenart",
627
+ value_name="Wert"
628
+ )
629
+ df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
630
+ "Annuität_NK": "Kapitalgebundene Kosten",
631
+ "Annuität_NV": "Bedarfsgebundene Kosten",
632
+ "Annuität_NB": "Betriebsgebundene Kosten"
633
+ })
634
+ kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
635
+ df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
636
+ color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
637
+ sortierte_names = df_mittel.sort_values("Annuität")["Name"]
638
+
639
+ st.markdown("### Mittlere annualisierte Kosten pro Heizsystem (Batch-Durchschnitt)")
640
+ st.altair_chart(
641
+ alt.Chart(df_stacked)
642
+ .mark_bar()
643
+ .encode(
644
+ x=alt.X("Wert:Q", title="mittlere annualisierte Kosten (€)", stack="zero"),
645
+ y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
646
+ color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
647
+ tooltip=["Name", "Kostenart", "Wert"]
648
+ ).properties(height=350, width=650),
649
+ use_container_width=True
650
+ )
651
+
652
+ # Download der Batch-CSV
653
+ csv_buffer = io.StringIO()
654
+ df_out.to_csv(csv_buffer, sep=";", index=False)
655
+ csv_bytes = csv_buffer.getvalue().encode("utf-8")
656
+ st.download_button(
657
+ "Download Ergebnis-CSV",
658
+ csv_bytes,
659
+ file_name="heizsysteme_batch_ergebnis.csv",
660
+ mime="text/csv"
661
+ )
662
+
663
+ # Einzelobjekt-Detailauswertung:
664
+ if st.session_state["annuitaeten_objekt_map_batch"] and len(st.session_state["annuitaeten_objekt_map_batch"]) > 0:
665
+ st.markdown("### Einzelanalyse eines Objekts (wie Einzel-Darstellung)")
666
+ objekt_ids = list(st.session_state["annuitaeten_objekt_map_batch"].keys())
667
+ col1, col2 = st.columns([5, 2])
668
+ with col1:
669
+ auswahl = st.selectbox(
670
+ "Objekt für Detaildarstellung auswählen", objekt_ids, key="batch_detail_sbox",
671
+ index=objekt_ids.index(st.session_state["selected_objekt_id_batch_detail"])
672
+ if st.session_state["selected_objekt_id_batch_detail"] in objekt_ids else 0
673
+ )
674
+ with col2:
675
+ if st.button("Kostenvergleich für ausgewähltes Objekt anzeigen", key="batch_detail_show_btn"):
676
+ st.session_state["selected_objekt_id_batch_detail"] = auswahl
677
+
678
+ show_id = st.session_state["selected_objekt_id_batch_detail"]
679
+ if show_id and show_id in st.session_state["annuitaeten_objekt_map_batch"]:
680
+ df_einzel = st.session_state["annuitaeten_objekt_map_batch"][show_id]
681
+ df_stacked = df_einzel[["Name", "Annuität_NK", "Annuität_NV", "Annuität_NB"]].melt(
682
+ id_vars="Name",
683
+ value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
684
+ var_name="Kostenart",
685
+ value_name="Wert"
686
+ )
687
+ df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
688
+ "Annuität_NK": "Kapitalgebundene Kosten",
689
+ "Annuität_NV": "Bedarfsgebundene Kosten",
690
+ "Annuität_NB": "Betriebsgebundene Kosten"
691
+ })
692
+ kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
693
+ df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
694
+ color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
695
+ sortierte_names = df_einzel.sort_values("Annuität")["Name"]
696
+
697
+ st.markdown(f"#### Annualisierte Kosten für Objekt {show_id}")
698
+ st.altair_chart(
699
+ alt.Chart(df_stacked)
700
+ .mark_bar()
701
+ .encode(
702
+ x=alt.X("Wert:Q", title="Annualisierte Kosten (€)", stack="zero"),
703
+ y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
704
+ color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
705
+ tooltip=["Name", "Kostenart", "Wert"]
706
+ ).properties(height=350, width=650),
707
+ use_container_width=True
708
+ )
709
+
710
+ st.markdown("---")
711
+ st.caption("Berechnung nach VDI 2067, Heizsystemauswahl gemäß DIN EN 15378.")
712
+
713
+
714
+
715
+ # do_calc = st.button("Batch-Berechnung starten")
716
+ # if do_calc:
717
+ # try:
718
+ # df_out = df_input.copy()
719
+ # max_count = 8
720
+ # annuität_colnames = [f"Heizsystem_{i+1}" for i in range(max_count)]
721
+ # hsystem_colnames = [f"Heizsystem_Name_{i+1}" for i in range(max_count)]
722
+ # for name, hname in zip(annuität_colnames, hsystem_colnames):
723
+ # df_out[name] = ""
724
+ # df_out[hname] = ""
725
+
726
+ # for idx, row in df_input.iterrows():
727
+ # try:
728
+ # nutzflaeche = row["Wohnflaeche"]
729
+ # baujahr = int(row["Baujahr"])
730
+ # gesamtbedarf = row.get("Gesamtwaermebedarf", "")
731
+ # spezbedarf = row.get("spezifischer Waermebedarf", "")
732
+ # if pd.notnull(gesamtbedarf) and gesamtbedarf != '' and float(gesamtbedarf) > 0 and nutzflaeche > 0:
733
+ # energiebedarf_spezifisch = float(gesamtbedarf) / float(nutzflaeche)
734
+ # else:
735
+ # energiebedarf_spezifisch = float(spezbedarf)
736
+ # df = finde_passende_heizsysteme(energiebedarf_spezifisch, baujahr, nutzflaeche, szen_kurz)
737
+ # df.columns = [
738
+ # "Name", "Leistung", "Investitionskosten", "Betriebsdauer", "Effizienz", "Emissionen",
739
+ # "Preisänderungsfaktor_Inv", "Betriebskosten", "Preisänderungsfaktor_Bedarf",
740
+ # "Fixkosten_O+M", "Preisänderungsfaktor_O+M", "Emissionsänderungsfaktor", "Förderung"
741
+ # ]
742
+ # for col in ["Investitionskosten", "Betriebsdauer", "Effizienz", "Emissionen",
743
+ # "Preisänderungsfaktor_Inv", "Betriebskosten", "Preisänderungsfaktor_Bedarf",
744
+ # "Fixkosten_O+M", "Preisänderungsfaktor_O+M", "Emissionsänderungsfaktor", "Förderung"]:
745
+ # df[col] = df[col].astype(str).str.replace(",", ".").str.replace("|", ".")
746
+ # df[col] = pd.to_numeric(df[col], errors='coerce')
747
+ # # Wenn Batch-global-Werte gesetzt sind, anwenden:
748
+ # batch_user_vals = st.session_state.get("batch_user_values")
749
+ # if batch_user_vals is not None:
750
+ # # Mapping via Name zur Sicherheit
751
+ # preview_heizsys_names = {preview_df.loc[x, "Name"]: x for x in preview_df.index}
752
+ # for pidx in batch_user_vals:
753
+ # sysname = preview_df.loc[pidx, "Name"]
754
+ # # Suche nach dem Namen im aktuellen DF
755
+ # df_idx = df[df["Name"] == sysname].index
756
+ # if not df_idx.empty:
757
+ # for key in batch_user_vals[pidx]:
758
+ # df.at[df_idx[0], key] = batch_user_vals[pidx][key]
759
+ # energiebedarf = energiebedarf_spezifisch
760
+ # df["Energiebedarf"] = df.apply(lambda rowh: calculate_energiebedarf(
761
+ # energiebedarf, nutzflaeche, rowh["Effizienz"]), axis=1)
762
+ # df["Annuität_NK"] = df.apply(
763
+ # lambda rowh: calculate_annuity_nk(rowh["Förderung"], rowh["Investitionskosten"], zinssatz, rowh["Betriebsdauer"],
764
+ # beobachtungszeitraum, rowh["Preisänderungsfaktor_Inv"]), axis=1)
765
+ # df["Annuität_NV"] = df.apply(
766
+ # lambda rowh: calculate_annuity_nv(
767
+ # float(rowh["Betriebskosten"]) - float(rowh["Emissionen"]) * emission_cost, rowh["Energiebedarf"], zinssatz,
768
+ # rowh["Preisänderungsfaktor_Bedarf"], beobachtungszeitraum,
769
+ # emission_cost, rowh["Emissionen"], preisaenderungsfaktor_emission, rowh["Emissionsänderungsfaktor"]
770
+ # ), axis=1)
771
+ # df["Annuität_NB"] = df.apply(
772
+ # lambda rowh: calculate_annuity_nb(rowh["Leistung"], rowh["Fixkosten_O+M"], rowh["Preisänderungsfaktor_O+M"],
773
+ # zinssatz, beobachtungszeitraum), axis=1)
774
+ # df["Annuität_NS"] = 0
775
+ # df["Annuität"] = df["Annuität_NK"] + df["Annuität_NV"] + df["Annuität_NB"] + df["Annuität_NS"]
776
+ # df = df.sort_values("Annuität")
777
+ # df_anni = df[[
778
+ # "Name", "Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"
779
+ # ]].copy()
780
+ # df_anni["Objekt-ID"] = row.get("Objekt-ID", idx)
781
+ # annuitaeten_gesamt.append(df_anni)
782
+ # annuitaeten_objekt_map[str(row.get("Objekt-ID", idx))] = df_anni
783
+
784
+ # for j in range(min(len(df), max_count)):
785
+ # df_out.at[idx, annuität_colnames[j]] = int(df.iloc[j]["Annuität"])
786
+ # df_out.at[idx, hsystem_colnames[j]] = df.iloc[j]["Name"]
787
+ # except Exception as e:
788
+ # st.warning(f"Objekt-ID {row.get('Objekt-ID', idx)}: {e}")
789
 
790
+ # heizsystem_namensmap = {
791
+ # "Ölheizung": "Oelheizung",
792
+ # "Luft-Wasser Wärmepumpe": "Luft-Wasser Waermepumpe",
793
+ # "Sole-Wasser Wärmepumpe": "Sole-Wasser Waermepumpe",
794
+ # "Wasser-Wasser Wärmepumpe": "Wasser-Wasser Waermepumpe",
795
+ # }
796
+ # for col in hsystem_colnames:
797
+ # df_out[col] = df_out[col].replace(heizsystem_namensmap)
798
 
799
+ # st.success("Berechnung abgeschlossen!")
800
 
801
+ # if annuitaeten_gesamt:
802
+ # # Verbinde alle Einzel-DFs zu einem großen Table
803
+ # df_alle = pd.concat(annuitaeten_gesamt, ignore_index=True)
804
+ # # Mittelwerte pro System
805
+ # df_mittel = df_alle.groupby("Name")[["Annuität_NK", "Annuität_NV", "Annuität_NB", "Annuität"]].mean(numeric_only=True).reset_index()
806
 
807
+ # # Für gestapeltes Balkendiagramm (wie im Einzel-Modus)
808
+ # df_stacked = df_mittel.melt(
809
+ # id_vars="Name",
810
+ # value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
811
+ # var_name="Kostenart",
812
+ # value_name="Wert"
813
+ # )
814
+ # df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
815
+ # "Annuität_NK": "Kapitalgebundene Kosten",
816
+ # "Annuität_NV": "Bedarfsgebundene Kosten",
817
+ # "Annuität_NB": "Betriebsgebundene Kosten"
818
+ # })
819
+ # kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
820
+ # df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
821
+ # color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
822
 
823
+ # # Heizsysteme nach mittlerer Gesamtkosten sortieren
824
+ # sortierte_names = df_mittel.sort_values("Annuität")["Name"]
825
 
826
+ # st.markdown("### Mittlere annualisierte Kosten pro Heizsystem (Batch-Durchschnitt)")
827
+ # st.altair_chart(
828
+ # alt.Chart(df_stacked)
829
+ # .mark_bar()
830
+ # .encode(
831
+ # x=alt.X("Wert:Q", title="mittlere annualisierte Kosten (€)", stack="zero"),
832
+ # y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
833
+ # color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
834
+ # tooltip=["Name", "Kostenart", "Wert"]
835
+ # ).properties(height=350, width=650),
836
+ # use_container_width=True
837
+ # )
838
 
839
+ # csv_buffer = io.StringIO()
840
+ # df_out.to_csv(csv_buffer, sep=";", index=False)
841
+ # csv_bytes = csv_buffer.getvalue().encode("utf-8")
842
+ # st.download_button(
843
+ # "Download Ergebnis-CSV",
844
+ # csv_bytes,
845
+ # file_name="heizsysteme_batch_ergebnis.csv",
846
+ # mime="text/csv"
847
+ # )
848
 
849
+ # # --- Einzelobjekt-Grafik nach dem Batch-Durchschnitt (immer NACH Mittelwert und Download-Button!) ---
850
 
851
+ # if annuitaeten_objekt_map and len(annuitaeten_objekt_map) > 0:
852
+ # st.markdown("### Einzelanalyse eines Objekts (wie Einzel-Darstellung)")
853
 
854
+ # objekt_ids = list(annuitaeten_objekt_map.keys())
855
+ # # EIGENER Key, EIGENE Session-Variable nur für diese Auswahl!
856
+ # col1, col2 = st.columns([5, 2])
857
+ # with col1:
858
+ # auswahl = st.selectbox(
859
+ # "Objekt für Detaildarstellung auswählen", objekt_ids, key="batch_detail_sbox",
860
+ # index=objekt_ids.index(st.session_state["selected_objekt_id_batch_detail"])
861
+ # if st.session_state["selected_objekt_id_batch_detail"] in objekt_ids else 0
862
+ # )
863
+ # with col2:
864
+ # if st.button("Kostenvergleich für ausgewähltes Objekt anzeigen", key="batch_detail_show_btn"):
865
+ # st.session_state["selected_objekt_id_batch_detail"] = auswahl
866
 
867
+ # # Nach dem Buttonklick bleibt das Diagramm sichtbar, bis ein neuer Klick gemacht wird
868
+ # show_id = st.session_state["selected_objekt_id_batch_detail"]
869
+ # if show_id and show_id in annuitaeten_objekt_map:
870
+ # df_einzel = annuitaeten_objekt_map[show_id]
871
+ # df_stacked = df_einzel[["Name", "Annuität_NK", "Annuität_NV", "Annuität_NB"]].melt(
872
+ # id_vars="Name",
873
+ # value_vars=["Annuität_NK", "Annuität_NV", "Annuität_NB"],
874
+ # var_name="Kostenart",
875
+ # value_name="Wert"
876
+ # )
877
+ # df_stacked["Kostenart"] = df_stacked["Kostenart"].replace({
878
+ # "Annuität_NK": "Kapitalgebundene Kosten",
879
+ # "Annuität_NV": "Bedarfsgebundene Kosten",
880
+ # "Annuität_NB": "Betriebsgebundene Kosten"
881
+ # })
882
+ # kostenart_order = ["Kapitalgebundene Kosten", "Bedarfsgebundene Kosten", "Betriebsgebundene Kosten"]
883
+ # df_stacked["Kostenart"] = pd.Categorical(df_stacked["Kostenart"], categories=kostenart_order, ordered=True)
884
+ # color_scale = alt.Scale(domain=kostenart_order, range=["#00386c", "#004c93", "#0069c8"])
885
+ # sortierte_names = df_einzel.sort_values("Annuität")["Name"]
886
 
887
+ # st.markdown(f"#### Annualisierte Kosten für Objekt {show_id}")
888
+ # st.altair_chart(
889
+ # alt.Chart(df_stacked)
890
+ # .mark_bar()
891
+ # .encode(
892
+ # x=alt.X("Wert:Q", title="Annualisierte Kosten (€)", stack="zero"),
893
+ # y=alt.Y("Name:N", title="Heizsystem", sort=list(sortierte_names)),
894
+ # color=alt.Color("Kostenart:N", scale=color_scale, title="Kostenart"),
895
+ # tooltip=["Name", "Kostenart", "Wert"]
896
+ # ).properties(height=350, width=650),
897
+ # use_container_width=True
898
+ # )
899
+
900
+ # except Exception as e:
901
+ # st.error(f"Fehler beim Einlesen/Berechnen: {e}")
902
 
903
+ # st.markdown("---")
904
+ # st.caption("Berechnung nach VDI 2067, Heizsystemauswahl gemäß DIN EN 15378.")
905
+ # else:
906
+ # st.warning("Bitte laden Sie eine CSV-Datei hoch", icon="⚠️")