Pandas DataFrame in mehrere DataFrames aufteilen mit ID

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
H3llo
User
Beiträge: 20
Registriert: Montag 9. Mai 2022, 10:17

Hallo,

ich habe eine Textdatei, in der mehrere Zeilen mit Daten sind. Der erste Eintrag einer Zeile beginnt immer mit "P1", "S1", "C1", "CA" oder "BC". Ich möchte dieses DataFrame nun in mehrere DataFrames aufteilen. Die Aufteilung soll entsprechend der eben geschilderten Zeilenanfänge erfolgen. Außerdem soll jedes DataFrame in Spalte 0 eine ID bekommen. Diese ID soll der Anzahl der Zeilen, die mit "P1" anfangen und oberhalb der aktuellen Zeile stehen, entsprechen. Ein Beispiel:

Die ersten Einträge der Textdatei sehen wie folgt aus:

P1ABC
S1EEF
S1DDF
C1AAA
CADDF
BCOIU
P1HDF
S1EEF
S1UUU
S1ZTZ
C1AAA
CADDF
C1AAB
BCOIV

Die neuen DataFrames sollen dann wie folgt aussehen:

1 P1ABC
2 P1HDF

1 S1EEF
1 S1DDF
2 S1EEF
2 S1UUU
2 S1ZTZ

1 C1AAA
2 C1AAA
2 C1AAB

1 CADDF
2 CADDF

1 BCOIU
2 BCOIV

Wie bekomme ich das so hin? (Wenn möglich ohne Schleife!)
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@H3llo: Du suchst die `groupby()`-Methode und die Methoden hinter `Series.str`. Die Beschreibung für die neue Spalte habe ich nicht verstanden.

Code: Alles auswählen

In [41]: df
Out[41]: 
        0
0   P1ABC
1   S1EEF
2   S1DDF
3   C1AAA
4   CADDF
5   BCOIU
6   P1HDF
7   S1EEF
8   S1UUU
9   S1ZTZ
10  C1AAA
11  CADDF
12  C1AAB
13  BCOIV

In [42]: list(df.groupby(df[0].str.slice(0, 2)))
Out[42]: 
[('BC',
          0
  5   BCOIU
  13  BCOIV),
 ('C1',
          0
  3   C1AAA
  10  C1AAA
  12  C1AAB),
 ('CA',
          0
  4   CADDF
  11  CADDF),
 ('P1',
         0
  0  P1ABC
  6  P1HDF),
 ('S1',
         0
  1  S1EEF
  2  S1DDF
  7  S1EEF
  8  S1UUU
  9  S1ZTZ)]
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
H3llo
User
Beiträge: 20
Registriert: Montag 9. Mai 2022, 10:17

Also nochmal zu den IDs. Letztendlich sollen die so aussehen:

1 P1ABC
1 S1EEF
1 S1DDF
1 C1AAA
1 CADDF
1 BCOIU
2 P1HDF
2 S1EEF
2 S1UUU
2 S1ZTZ
2 C1AAA
2 CADDF
2 C1AAB
2 BCOIV

Die ID soll also dann eins hoch gezählt werden, wenn die Zeile mit "P1" beginnt. Und das ganze ohne Schleife.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Du suchst also eine Kombination aus str.startswith und cumsum.
H3llo
User
Beiträge: 20
Registriert: Montag 9. Mai 2022, 10:17

Keine Ahnung :) Könntest du etwas genauer ausführen, wie eine Lösung damit aussehen könnte?
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@H3llo: Man könnte eine neue Spalte mit 0en einführen, da überall wo etwas in der Textspalte mit "P1" anfängt eine 1 setzen, und dann mit `cumsum()` über die Spalte laufen.

Edit: Geht sogar etwas einfacher, weil `True` und `False` ja im Grunde auch die Zahlen 1 und 0 sind, kann man `cumsum()` gleich auf das Ergebnis von dem Test ob die Werte mit "P1" anfangen, anwenden und das dann als weitere Spalte setzen:

Code: Alles auswählen

In [10]: df                                                                     
Out[10]: 
        0
0   P1ABC
1   S1EEF
2   S1DDF
3   C1AAA
4   CADDF
5   BCOIU
6   P1HDF
7   S1EEF
8   S1UUU
9   S1ZTZ
10  C1AAA
11  CADDF
12  C1AAB
13  BCOIV

In [11]: df[0].str.startswith("P1")                                             
Out[11]: 
0      True
1     False
2     False
3     False
4     False
5     False
6      True
7     False
8     False
9     False
10    False
11    False
12    False
13    False
Name: 0, dtype: bool

In [12]: df[0].str.startswith("P1").cumsum()                                    
Out[12]: 
0     1
1     1
2     1
3     1
4     1
5     1
6     2
7     2
8     2
9     2
10    2
11    2
12    2
13    2
Name: 0, dtype: int64

In [13]: df[1] = df[0].str.startswith("P1").cumsum()                            

In [14]: df                                                                     
Out[14]: 
        0  1
0   P1ABC  1
1   S1EEF  1
2   S1DDF  1
3   C1AAA  1
4   CADDF  1
5   BCOIU  1
6   P1HDF  2
7   S1EEF  2
8   S1UUU  2
9   S1ZTZ  2
10  C1AAA  2
11  CADDF  2
12  C1AAB  2
13  BCOIV  2
Man sollte als Spaltenindex aber besser sinnvolle Namen für die Spalten verwenden statt nichtssagender Nummern. Das Problem was das löst wird deutlich wenn man sich klar macht, das *ich* das nicht machen kann, weil ich keine Ahnung habe was das bedeuten soll, eben weil die Spalten keine vernünftigen Bezeichnungen haben, sondern nichtssagende Nummern.
Zuletzt geändert von __blackjack__ am Donnerstag 1. September 2022, 13:38, insgesamt 1-mal geändert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
H3llo
User
Beiträge: 20
Registriert: Montag 9. Mai 2022, 10:17

Mit welcher Funktion könnte man die 1 in den Zeilen mit "P1" setzen?
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@H3llo: Ich hatte da noch eine einfachere Möglichkeit ergänzt. Ansonsten solltest Du mal das/die Tutorials aus der Pandas-Dokumentation durcharbeiten. Aus was so ein `DataFrame` besteht, wie man Spalten und Zeilen selektieren kann, und auch wie man ausgewählten Zellen in einer Spalte neue Werte zuweisen kann, und so weiter.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
H3llo
User
Beiträge: 20
Registriert: Montag 9. Mai 2022, 10:17

@blackjack: Hatte schon geantwortet, als du deinen Edit veröffentlicht hast.

Vielen Dank! Das ist genau das, was ich gesucht habe. Tolle Lösung!
Antworten