Python Pandas - Kombinieren von mehreren Spalten CSV

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
Digisven
User
Beiträge: 5
Registriert: Freitag 23. Juni 2023, 12:11

Hallo zusammen. :)

Ich versuche in Python mehrere Spalten aus einer CSV-Datei zusammenzufügen und in eine neue Spalte in einer neuen CSV-Datei abzuspeichern.

So würde mein Code aussehen:

Code: Alles auswählen

import pandas as pd

# Load the CSV file into a Pandas DataFrame
df = pd.read_csv("Test.csv", sep=r'\\t', engine='python')

# Combine cells in a specific column
df['Name'] = df[['Name_1', 'Name_2', 'Name_3']].apply(' '.join, axis=1)

# Save the result to a new CSV file
df.to_csv("Test-new.csv", index=False)
Wenn ich das ausführe, kommt folgender Fehler:

Code: Alles auswählen

C:\Users\s.123\Downloads\python>Test3.py
Traceback (most recent call last):
  File "C:\Users\s.123\Downloads\python\Test.py", line 7, in <module>
    df['Name'] = df[['Name_1', 'Name_2', 'Name_3']].apply(' '.join, axis=1)
                 ~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\s.123\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\core\frame.py", line 3767, in __getitem__
    indexer = self.columns._get_indexer_strict(key, "columns")[1]
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\s.123\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\core\indexes\base.py", line 5876, in _get_indexer_strict
    self._raise_if_missing(keyarr, indexer, axis_name)
  File "C:\Users\s.123\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\core\indexes\base.py", line 5935, in _raise_if_missing
    raise KeyError(f"None of [{key}] are in the [{axis_name}]")
KeyError: "None of [Index(['Name_1', 'Name_2', 'Name_3'], dtype='object')] are in the [columns]"
Die CSV sieht so aus: https://filehorst.de/d/erbwxyuJ

oder hier als Code, getrennt mit Tab:

Code: Alles auswählen

LFNR	Name_1	Name_2	Name_3	Strasse	Plz	Ort	Telefon	Telefax
4		Wxxxxx.xe	KxxxxY- XxY XxxxxxxxxxxY	XxxxxxxxxxxxxxxxxY 25	11111	XxxxxxY	0123-1231230	0123-1231231
24	Fxxxx	Kxxxx Rxxxxxxxxxxxxxxxk xxY	Hxxxxxxxxxxxf Xxxx X xx. XX	Ix Xxxxxxh 5	0123	KxxxY	012312-12312	012312-123123
25	 	 	Bxxxxxxxxxx Kxxxxxx	Sxxxxxxxxxxxx Sxxxxx 123	0123	Cxxxxxx	0123 123123	0123 1231231
Hoffe jemand eine Idee. :)

Vielen Dank schonmal. :)
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Digisven: Na dann schau Dir doch mal den Dataframe nach dem laden an. Der enthält ja offensichtlich nicht das was er soll. Warum wird das einlesen denn überhaupt so kompliziert gemacht? Warum nicht einfach "\t" als Trennzeichen angeben und die `engine` weg lassen?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Digisven
User
Beiträge: 5
Registriert: Freitag 23. Juni 2023, 12:11

Hmm, ok danke erstmal. Also ich verstehe, dass er etwas anderes erwartet nur leider irgendwie nicht was.

Ich hab das nun mal eben ganz dumm gebaut, er soll es einlesen und wieder so ausgeben wie sie vorher war, hiermit:

Code: Alles auswählen

import pandas as pd

# Load the CSV file into a Pandas DataFrame
df = pd.read_csv("Test.csv", sep=r'\\t', engine='python')

# Save the result to a new CSV file
df.to_csv("Test-new.csv", index=False)
Wenn ich das mit der engine weglasse, bekomme ich eine Warnung, er macht es trotzdem aber es ist trotzdem nicht die genau gleiche CSV wie sie reinkam, am Ende fehlen Spalten/Tabs, die scheinbar leer waren.

Wenn ich das sep=r'\\t', engine='python') zu sep='\t') ändere bekomme ich zwar keinen Fehler, aber er haut mir die Tabs alle weg, es soll so erstmal genau so rauskommen wie rein.

Das wär also vielleicht die erste Übung, wie bekomm ich sie genau so wieder raus, wie sie rein ging. Und dann sollte ich glaube ich erst Änderungen vornehmen.

Oder was meint ihr? :)

Vielen Dank schon mal.
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Digisven: CSV heisst ursprünglich „comma separated values“. Statt Komma verwenden die Leute aus praktischen Gründen manchmal andere Trennzeichen. Die muss man dann angeben. In der Ausgangsdatei hier zum Beispiel das Tabulator-Zeichen ("\t"). `to_csv()` heisst trotzdem immer erst einmal „to *comma* separated values“. Falls Du da etwas anderes als Kommas als Trennzeichen haben möchtest, musst Du das explizit angeben.

*Genau* so wie es rein ging muss man es übrigens nicht heraus bekommen. Die serialisierte Form der *gleichen* Daten kann durchaus leicht unterschiedlich aussehen. Und falls Spalten mit Zahlen darin vorkommen, kann man auch nicht erwarten das die immer genau so geschrieben werden wie die gelesen wurden. Mindestens mal führende und folgende Nullen weglassen ändert beispielsweise nichts am Wert an sich.

Da ist hier übrigens ein Fallstrick in den Daten: die Postleitzahl. Pandas hat keine Ahnung was das ist, wird diese Zeile aber sehr wahrscheinlich als numerische Spalte ”erraten”, was aber falsch ist, und die führenden Nullen verschwinden lassen wird. Die Telefonnummern und das Telefax sind da potentiell auch betroffen, falls man Daten hat bei denen da mal keine Zeichen in mindestens einem Datensatz enthalten sind, die das zu einer nicht-numerischen Spalte machen. Du musst also explizit mindestens bei diesen Spalten den Datentyp selbst vorgeben, wenn das robuster Code werden soll.

Warum überhaupt Pandas? Manchmal habe ich das Gefühl, das ist das programmatische Äquivalent zu Excel und wird für Sachen missbraucht, für die es eigentlich gar nicht gedacht ist. Es gibt ein `csv`-Modul in der Standardbibliothek.

Weitere Baustelle ist dann auch noch die Kodierung. Wenn man keine angibt muss man nehmen was kommt, und leider ersetzt Pandas dann nicht dekodierbare Zeichen still und heimlich durch das Ersetzungszeichen "�". Egal was man verwendet um Textdateien zu verarbeiten, man muss immer schauen was mit der Kodierung passiert. Sowohl beim lesen als auch beim schreiben. Bei reinen Zahlentabellen, oder wenn einen die Texte nicht wirklich interessieren bei einer Auswertung mag das kein Problem sein, aber bei Namen und Adressen und einer Ausgabedatei die weiterverarbeitet werden soll, muss man auf Umlaute & Co natürlich achten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17759
Registriert: Sonntag 21. Oktober 2012, 17:20

Natürlich werden die Tabs weggehauen, denn die sind das Trennzeichen. \\t sind nicht das Trennzeichenpaar. Die C-Einleseroutine kann nur mit einzelnen Zeichen umgehen, die Python-Engine auch mit Trennzeichenketten, hast Du aber nicht.

Wie hast Du die Tabelle geschrieben? Wie eingelesen? Was ist der Input? Was der Output? Und was stimmt am Output nicht mit Deinen Erwartungen überein?
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hier mal ohne Pandas und ohne die Probleme die das macht, bezüglich automagisch Zahlenspalten erkennen und still und leise Zeichen verwerfen wenn man keine Kodierung angibt (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import csv

DELIMITER = "\t"
ENCODING = "utf-8"

SOURCE_SLICE = slice(1, 4)
SOURCE_COLUMN_NAMES = ["Name_1", "Name_2", "Name_3"]
assert (
    SOURCE_SLICE.step == 1
    and SOURCE_SLICE.stop - SOURCE_SLICE.start == len(SOURCE_COLUMN_NAMES)
)
NEW_COLUMN_NAME = "Name"


def main():
    with open("Test.csv", "r", encoding=ENCODING, newline="") as input_file:
        rows = csv.reader(input_file, delimiter=DELIMITER)
        headers = next(rows)
        if (
            headers[SOURCE_SLICE] != SOURCE_COLUMN_NAMES
            or NEW_COLUMN_NAME in headers
        ):
            raise ValueError(f"unexpected headers: {headers!r}")

        headers.append(NEW_COLUMN_NAME)
        with open(
            "Test-new.csv", "w", encoding=ENCODING, newline=""
        ) as output_file:
            writer = csv.writer(output_file, delimiter=DELIMITER)
            writer.writerow(headers)
            for row in rows:
                #
                # FIXME Das ist ziemlich wahrscheinlich nicht ganz das was
                # gewollt ist.  Steht aber so im Originalbeitrag.
                # 
                row.append(" ".join(row[SOURCE_SLICE]))
                writer.writerow(row)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Digisven
User
Beiträge: 5
Registriert: Freitag 23. Juni 2023, 12:11

Hallo und vielen Dank erstmal und tut mir Leid, etwas spät dran. :)

Wenn ich den Code ausführe kommt dieses hier:

Code: Alles auswählen

C:\python>python Test6.py
Traceback (most recent call last):
  File "C:\python\Test6.py", line 10, in <module>
    and SOURCE_SLICE.stop - SOURCE_SLICE.start == len(SOURCE_COLUMN_NAMES)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
Behauptungs-Fehler, hm... ich weiß nicht genau was er möchte. :/
Benutzeravatar
grubenfox
User
Beiträge: 433
Registriert: Freitag 2. Dezember 2022, 15:49

Ungünstige Kombination von Prüfungen. Er scheitert nämlich am

Code: Alles auswählen

SOURCE_SLICE.step == 1
Der .step ist hier None ....
Digisven
User
Beiträge: 5
Registriert: Freitag 23. Juni 2023, 12:11

Ah, vielen Dank. :)

Ok, nun hat es scheinbar wirklich funktioniert. Er hat die Felder Name_1, Name_2, Name_3 zusammengefügt.

Fein. :) Aber wie auch geschrieben wurde von __blackjack__ im Code als Kommentar, ist es wohl nicht ganz das was ich wollte.

Ich hatte scheinbar wirklich vergessen zu sagen, dass die eben nicht nur zusammengefügt und angehängt werden soll,

sondern dann eben auch Name_1, Name_2, Name_3 dann verschwinden soll und das neue Name vorne stehen soll, wo die 3 mal standen. :)

Ansonsten bin ich schon sehr dankbar soweit für´s Ergebnis. :)
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Digisven: Mit dem Kommentar war schon der Inhalt der neuen Spalte gemeint. Da sind am Ende Leerzeichen drin, die wahrscheinlich nicht gewollt sind, wenn Spalten leer sind.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Digisven
User
Beiträge: 5
Registriert: Freitag 23. Juni 2023, 12:11

Hallo __blackjack__ :)

Oh stimmt, die sind mir gar nicht aufgefallen. Hm... ok, die sind auch nicht gewollt, stimmt.
Antworten