Wie man große CSV-Dateien in Python effizient verarbeitet

PythonPythonBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Die Verarbeitung großer CSV-Dateien ist eine häufige Herausforderung für Python-Entwickler. Dieses Tutorial führt Sie durch effiziente Techniken zur effektiven Verarbeitung dieser Dateien und konzentriert sich dabei auf die Optimierung der Leistung und des Speicherverbrauchs. Am Ende dieses Labs verfügen Sie über praktisches Wissen, um datenintensive CSV-Verarbeitungsaufgaben in Python zu bewältigen.

Unabhängig davon, ob Sie Kundendaten analysieren, Finanzunterlagen verarbeiten oder mit einer anderen Art von strukturierten Daten arbeiten, helfen Ihnen die in diesem Lab erlernten Fähigkeiten, große Datensätze effizient zu verarbeiten, ohne auf Speicherprobleme zu stoßen.

Erstellen und Lesen einer einfachen CSV-Datei

CSV (Comma-Separated Values) ist ein beliebtes Dateiformat zur Speicherung tabellarischer Daten. In diesem Schritt erstellen wir eine einfache CSV-Datei und lernen, wie man sie mit dem in Python integrierten csv-Modul liest.

CSV-Dateien verstehen

Eine CSV-Datei speichert Daten in einem Nur-Text-Format, wobei:

  • Jede Zeile eine Datenzeile darstellt
  • Werte innerhalb jeder Zeile durch ein Trennzeichen (typischerweise ein Komma) getrennt sind
  • Die erste Zeile oft Spaltenüberschriften enthält

Beginnen wir mit der Erstellung einer einfachen CSV-Datei, mit der wir arbeiten können.

Erstellen einer CSV-Datei

Erstellen wir zunächst ein Verzeichnis, in dem wir arbeiten, und erstellen Sie dann eine einfache CSV-Datei:

  1. Öffnen Sie das Terminal in der WebIDE
  2. Erstellen Sie im Editor eine neue Python-Datei mit dem Namen csv_basics.py

Fügen Sie nun den folgenden Code zu csv_basics.py hinzu:

import csv

## Data to write to CSV
data = [
    ['Name', 'Age', 'City'],              ## Header row
    ['John Smith', '28', 'New York'],
    ['Sarah Johnson', '32', 'San Francisco'],
    ['Michael Brown', '45', 'Chicago'],
    ['Emily Davis', '36', 'Boston'],
    ['David Wilson', '52', 'Seattle']
]

## Writing data to a CSV file
with open('sample_data.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(data)

print("CSV file 'sample_data.csv' created successfully.")

Führen Sie diesen Code aus, indem Sie den folgenden Befehl im Terminal ausführen:

python3 csv_basics.py

Erwartete Ausgabe:

CSV file 'sample_data.csv' created successfully.

Dadurch wird im aktuellen Verzeichnis eine neue CSV-Datei mit dem Namen sample_data.csv erstellt. Sie können den Inhalt dieser Datei anzeigen, indem Sie Folgendes ausführen:

cat sample_data.csv

Erwartete Ausgabe:

Name,Age,City
John Smith,28,New York
Sarah Johnson,32,San Francisco
Michael Brown,45,Chicago
Emily Davis,36,Boston
David Wilson,52,Seattle

Lesen einer CSV-Datei

Lesen wir nun die CSV-Datei, die wir gerade erstellt haben. Erstellen Sie eine neue Datei mit dem Namen read_csv.py mit dem folgenden Code:

import csv

## Reading a CSV file
with open('sample_data.csv', 'r') as file:
    reader = csv.reader(file)

    print("Contents of sample_data.csv:")
    print("--------------------------")

    for row in reader:
        print(row)

## Reading and accessing specific columns
print("\nReading specific columns:")
print("--------------------------")

with open('sample_data.csv', 'r') as file:
    reader = csv.reader(file)
    headers = next(reader)  ## Skip the header row

    for row in reader:
        name = row[0]
        age = row[1]
        city = row[2]
        print(f"Name: {name}, Age: {age}, City: {city}")

Führen Sie diesen Code mit Folgendem aus:

python3 read_csv.py

Erwartete Ausgabe:

Contents of sample_data.csv:
--------------------------
['Name', 'Age', 'City']
['John Smith', '28', 'New York']
['Sarah Johnson', '32', 'San Francisco']
['Michael Brown', '45', 'Chicago']
['Emily Davis', '36', 'Boston']
['David Wilson', '52', 'Seattle']

Reading specific columns:
--------------------------
Name: John Smith, Age: 28, City: New York
Name: Sarah Johnson, Age: 32, City: San Francisco
Name: Michael Brown, Age: 45, City: Chicago
Name: Emily Davis, Age: 36, City: Boston
Name: David Wilson, Age: 52, City: Seattle

Das CSV-Modul verstehen

Das Python csv-Modul stellt zwei Hauptklassen bereit:

  • csv.reader: Liest CSV-Dateien und gibt jede Zeile als Liste von Strings zurück
  • csv.writer: Schreibt Daten in CSV-Dateien

Dieses Modul behandelt alle Komplexitäten im Umgang mit verschiedenen CSV-Formaten, wie z. B. das Escapen von Sonderzeichen und die Behandlung von Anführungszeichen.

In diesem Schritt haben Sie gelernt, wie man eine einfache CSV-Datei erstellt und liest. Im nächsten Schritt werden wir effizientere Möglichkeiten zur Verarbeitung größerer CSV-Dateien untersuchen.

Verwendung von DictReader für die komfortable CSV-Verarbeitung

Im vorherigen Schritt haben wir mit den grundlegenden Funktionen csv.reader und csv.writer gearbeitet. Lassen Sie uns nun eine bequemere Methode zur Verarbeitung von CSV-Dateien mit der Klasse csv.DictReader untersuchen, die besonders nützlich ist, wenn Sie mit Daten arbeiten, die Spaltenüberschriften haben.

Was ist DictReader?

csv.DictReader liest CSV-Dateien und gibt jede Zeile als Dictionary zurück, wobei:

  • Die Schlüssel aus den Spaltenüberschriften (standardmäßig die erste Zeile der CSV-Datei) entnommen werden
  • Die Werte die entsprechenden Daten aus jeder Zeile sind

Dieser Ansatz macht Ihren Code lesbarer und weniger fehleranfällig, da Sie auf Spalten nach Namen anstatt nach Index verweisen können.

Erstellen einer größeren Testdatei

Erstellen wir zunächst eine etwas größere CSV-Datei, um die Vorteile von DictReader zu demonstrieren. Erstellen Sie eine neue Datei mit dem Namen create_users_data.py mit dem folgenden Code:

import csv
import random

## Generate some sample user data
def generate_users(count):
    users = [['id', 'name', 'email', 'age', 'country']]  ## Header row

    domains = ['gmail.com', 'yahoo.com', 'outlook.com', 'example.com']
    countries = ['USA', 'Canada', 'UK', 'Australia', 'Germany', 'France', 'Japan', 'Brazil']
    first_names = ['John', 'Jane', 'Michael', 'Emily', 'David', 'Sarah', 'Robert', 'Lisa']
    last_names = ['Smith', 'Johnson', 'Brown', 'Davis', 'Wilson', 'Miller', 'Jones', 'Taylor']

    for i in range(1, count + 1):
        first_name = random.choice(first_names)
        last_name = random.choice(last_names)
        name = f"{first_name} {last_name}"
        email = f"{first_name.lower()}.{last_name.lower()}@{random.choice(domains)}"
        age = random.randint(18, 65)
        country = random.choice(countries)

        users.append([str(i), name, email, str(age), country])

    return users

## Create a CSV file with 100 users
users_data = generate_users(100)

## Write data to CSV file
with open('users_data.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(users_data)

print(f"Created 'users_data.csv' with 100 user records")

Führen Sie das Skript aus, um die Datei zu erstellen:

python3 create_users_data.py

Erwartete Ausgabe:

Created 'users_data.csv' with 100 user records

Untersuchen wir die ersten Zeilen dieser neuen Datei:

head -n 5 users_data.csv

Sie sollten die Header-Zeile gefolgt von 4 Datenzeilen sehen:

id,name,email,age,country
1,John Smith,[email protected],25,USA
2,Emily Brown,[email protected],32,Canada
3,David Jones,[email protected],45,UK
4,Sarah Wilson,[email protected],28,Australia

Verwenden von DictReader zur Verarbeitung der CSV-Datei

Erstellen wir nun ein Skript, um diese Datei mit DictReader zu verarbeiten. Erstellen Sie eine neue Datei mit dem Namen dict_reader_example.py mit dem folgenden Code:

import csv

## Read the CSV file using DictReader
with open('users_data.csv', 'r') as file:
    csv_reader = csv.DictReader(file)

    ## Print the field names (column headers)
    print(f"Column headers: {csv_reader.fieldnames}")
    print("\nFirst 5 records:")
    print("-----------------")

    ## Print the first 5 records
    for i, row in enumerate(csv_reader):
        if i < 5:
            ## Access fields by name
            print(f"User {row['id']}: {row['name']}, {row['age']} years old, from {row['country']}")
            print(f"  Email: {row['email']}")
        else:
            break

## Using DictReader for data analysis
with open('users_data.csv', 'r') as file:
    csv_reader = csv.DictReader(file)

    ## Calculate average age
    total_age = 0
    user_count = 0

    ## Count users by country
    countries = {}

    for row in csv_reader:
        user_count += 1
        total_age += int(row['age'])

        ## Count users by country
        country = row['country']
        if country in countries:
            countries[country] += 1
        else:
            countries[country] = 1

    avg_age = total_age / user_count if user_count > 0 else 0

    print("\nData Analysis:")
    print("--------------")
    print(f"Total users: {user_count}")
    print(f"Average age: {avg_age:.2f} years")
    print("\nUsers by country:")

    for country, count in sorted(countries.items(), key=lambda x: x[1], reverse=True):
        print(f"  {country}: {count} users")

Führen Sie dieses Skript aus:

python3 dict_reader_example.py

Erwartete Ausgabe (Ihre genauen Werte können variieren, da die Daten zufällig generiert werden):

Column headers: ['id', 'name', 'email', 'age', 'country']

First 5 records:
-----------------
User 1: John Smith, 25 years old, from USA
  Email: [email protected]
User 2: Emily Brown, 32 years old, from Canada
  Email: [email protected]
User 3: David Jones, 45 years old, from UK
  Email: [email protected]
User 4: Sarah Wilson, 28 years old, from Australia
  Email: [email protected]
User 5: Michael Taylor, 37 years old, from Germany
  Email: [email protected]

Data Analysis:
--------------
Total users: 100
Average age: 41.35 years

Users by country:
  USA: 16 users
  Canada: 14 users
  Japan: 13 users
  UK: 12 users
  Germany: 12 users
  Australia: 12 users
  France: 11 users
  Brazil: 10 users

Vorteile der Verwendung von DictReader

Wie Sie sehen können, bietet die Verwendung von DictReader mehrere Vorteile:

  1. Lesbarer Code: Sie können auf Felder nach Namen zugreifen, anstatt sich Indexpositionen zu merken
  2. Selbstdokumentierend: Der Code zeigt deutlich, auf welches Feld Sie zugreifen
  3. Flexibilität: Wenn sich die Spaltenreihenfolge in der CSV-Datei ändert, funktioniert Ihr Code weiterhin, solange die Spaltennamen gleich bleiben

Dieser Ansatz ist besonders nützlich, wenn Sie mit realen Daten arbeiten, die viele Spalten haben, oder wenn sich die Spaltenreihenfolge im Laufe der Zeit ändern kann.

Im nächsten Schritt werden wir effiziente Techniken zur Verarbeitung größerer CSV-Dateien untersuchen, ohne alles auf einmal in den Speicher zu laden.

Effizientes Verarbeiten großer CSV-Dateien

In realweltlichen Szenarien müssen Sie möglicherweise CSV-Dateien verarbeiten, die mehrere Gigabyte groß sind. Das vollständige Laden solcher Dateien in den Speicher kann dazu führen, dass Ihre Anwendung abstürzt oder sich erheblich verlangsamt. In diesem Schritt werden wir Techniken zur effizienten Verarbeitung großer CSV-Dateien untersuchen.

Die Speicherherausforderung bei großen Dateien

Bei der Arbeit mit CSV-Dateien gibt es drei gängige Ansätze, die jeweils unterschiedliche Speicheranforderungen haben:

  1. Laden der gesamten Datei in den Speicher - Einfach, aber verbraucht den meisten Speicher
  2. Streamen der Datei Zeile für Zeile - Verbraucht minimalen Speicher, kann aber für komplexe Operationen langsamer sein
  3. Chunking (Aufteilen in Blöcke) - Ein Mittelweg, der die Datei in handhabbaren Blöcken verarbeitet

Lassen Sie uns jeden dieser Ansätze anhand praktischer Beispiele untersuchen.

Erstellen einer größeren Beispieldatei

Erstellen wir zunächst eine größere Beispieldatei, um diese Techniken zu demonstrieren. Erstellen Sie eine neue Datei mit dem Namen create_large_csv.py:

import csv
import random
from datetime import datetime, timedelta

def create_large_csv(filename, num_rows):
    ## Define column headers
    headers = ['transaction_id', 'date', 'customer_id', 'product_id', 'amount', 'status']

    ## Create a file with the specified number of rows
    with open(filename, 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(headers)

        ## Generate random data
        start_date = datetime(2022, 1, 1)
        status_options = ['completed', 'pending', 'failed', 'refunded']

        for i in range(1, num_rows + 1):
            ## Generate random values
            transaction_id = f"TXN-{i:08d}"
            days_offset = random.randint(0, 365)
            date = (start_date + timedelta(days=days_offset)).strftime('%Y-%m-%d')
            customer_id = f"CUST-{random.randint(1001, 9999)}"
            product_id = f"PROD-{random.randint(101, 999)}"
            amount = round(random.uniform(10.0, 500.0), 2)
            status = random.choice(status_options)

            ## Write row to CSV
            writer.writerow([transaction_id, date, customer_id, product_id, amount, status])

            ## Print progress indicator for every 10,000 rows
            if i % 10000 == 0:
                print(f"Generated {i} rows...")

## Create a CSV file with 50,000 rows (about 5-10 MB)
create_large_csv('transactions.csv', 50000)
print("Large CSV file 'transactions.csv' has been created.")

Führen Sie dieses Skript aus, um die Datei zu erstellen:

python3 create_large_csv.py

Erwartete Ausgabe:

Generated 10000 rows...
Generated 20000 rows...
Generated 30000 rows...
Generated 40000 rows...
Generated 50000 rows...
Large CSV file 'transactions.csv' has been created.

Sie können die Größe der Datei mit folgendem Befehl überprüfen:

ls -lh transactions.csv

Erwartete Ausgabe (die Größe kann leicht variieren):

-rw-r--r-- 1 labex labex 3.8M Apr 15 12:30 transactions.csv

Ansatz 1: Zeilenweise Verarbeitung (Streaming)

Der speichereffizienteste Ansatz ist die zeilenweise Verarbeitung einer CSV-Datei. Erstellen Sie eine Datei mit dem Namen streaming_example.py:

import csv
import time

def process_csv_streaming(filename):
    print(f"Processing {filename} using streaming (line by line)...")
    start_time = time.time()

    ## Track some statistics
    row_count = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}

    ## Process the file line by line
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)

        for row in reader:
            ## Increment row counter
            row_count += 1

            ## Process row data
            amount = float(row['amount'])
            status = row['status']

            ## Update statistics
            total_amount += amount
            status_counts[status] += 1

    ## Calculate and display results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults:")
    print(f"Processed {row_count:,} rows in {processing_time:.2f} seconds")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/row_count:.2f}")
    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / row_count) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

## Process the file
process_csv_streaming('transactions.csv')

Führen Sie dieses Skript aus:

python3 streaming_example.py

Erwartete Ausgabe (Ihre genauen Zahlen können variieren):

Processing transactions.csv using streaming (line by line)...

Results:
Processed 50,000 rows in 0.17 seconds
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Ansatz 2: Chunked Processing (Blockweise Verarbeitung)

Für komplexere Operationen oder wenn Sie Daten in Batches verarbeiten müssen, können Sie einen Chunked-Ansatz verwenden. Erstellen Sie eine Datei mit dem Namen chunked_example.py:

import csv
import time

def process_csv_chunked(filename, chunk_size=10000):
    print(f"Processing {filename} using chunks of {chunk_size} rows...")
    start_time = time.time()

    ## Track some statistics
    row_count = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}

    ## Process the file in chunks
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)

        chunk = []
        for row in reader:
            ## Add row to current chunk
            chunk.append(row)

            ## When chunk reaches desired size, process it
            if len(chunk) >= chunk_size:
                ## Process the chunk
                for row_data in chunk:
                    ## Update statistics
                    row_count += 1
                    amount = float(row_data['amount'])
                    status = row_data['status']

                    total_amount += amount
                    status_counts[status] += 1

                print(f"Processed chunk of {len(chunk)} rows... ({row_count:,} total)")
                ## Clear the chunk for next batch
                chunk = []

        ## Process any remaining rows in the last chunk
        if chunk:
            for row_data in chunk:
                row_count += 1
                amount = float(row_data['amount'])
                status = row_data['status']

                total_amount += amount
                status_counts[status] += 1

            print(f"Processed final chunk of {len(chunk)} rows... ({row_count:,} total)")

    ## Calculate and display results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults:")
    print(f"Processed {row_count:,} rows in {processing_time:.2f} seconds")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/row_count:.2f}")
    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / row_count) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

## Process the file with chunks of 10,000 rows
process_csv_chunked('transactions.csv', chunk_size=10000)

Führen Sie dieses Skript aus:

python3 chunked_example.py

Erwartete Ausgabe:

Processing transactions.csv using chunks of 10000 rows...
Processed chunk of 10000 rows... (10,000 total)
Processed chunk of 10000 rows... (20,000 total)
Processed chunk of 10000 rows... (30,000 total)
Processed chunk of 10000 rows... (40,000 total)
Processed chunk of 10000 rows... (50,000 total)

Results:
Processed 50,000 rows in 0.20 seconds
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Speichervergleich

Die wesentlichen Unterschiede zwischen diesen Ansätzen sind:

  1. Streaming (Zeile für Zeile):

    • Verwendet unabhängig von der Dateigröße minimalen Speicher
    • Am besten für sehr große Dateien geeignet
    • Einfache Operationen für jede Zeile
  2. Chunked Processing (Blockweise Verarbeitung):

    • Verwendet mehr Speicher als Streaming, ist aber immer noch effizient
    • Gut für Operationen, die Zeilen in Batches verarbeiten müssen
    • Bietet Fortschrittsaktualisierungen während der Verarbeitung
    • Kann für die parallele Verarbeitung mit Multiprocessing kombiniert werden

Für die meisten praktischen Zwecke wird der Streaming-Ansatz empfohlen, es sei denn, Sie benötigen explizit Batch-Verarbeitungsfunktionen. Er bietet die beste Speichereffizienz bei gleichzeitig guter Leistung.

Im nächsten Schritt werden wir die Verwendung von Bibliotheken von Drittanbietern wie Pandas für noch leistungsfähigere CSV-Verarbeitungsfunktionen untersuchen.

Verwendung von Pandas für die erweiterte CSV-Verarbeitung

Während das eingebaute csv-Modul von Python leistungsstark ist, bietet die pandas-Bibliothek erweiterte Funktionen für Datenanalyse und -manipulation. In diesem Schritt werden wir untersuchen, wie man Pandas für die effiziente CSV-Verarbeitung verwendet.

Installieren von Pandas

Installieren wir zunächst die Pandas-Bibliothek:

pip install pandas

Erwartete Ausgabe:

Collecting pandas
  Downloading pandas-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3/12.3 MB 42.6 MB/s eta 0:00:00
...
Successfully installed pandas-2.0.0 numpy-1.24.3 python-dateutil-2.8.2 pytz-2023.3 tzdata-2023.3

Lesen von CSV-Dateien mit Pandas

Pandas macht es einfach, CSV-Daten zu lesen, zu analysieren und zu manipulieren. Erstellen Sie eine Datei mit dem Namen pandas_basic.py:

import pandas as pd
import time

def process_with_pandas(filename):
    print(f"Processing {filename} with pandas...")
    start_time = time.time()

    ## Read the CSV file into a DataFrame
    df = pd.read_csv(filename)

    ## Display basic information
    print(f"\nDataFrame Info:")
    print(f"Shape: {df.shape} (rows, columns)")
    print(f"Column names: {', '.join(df.columns)}")

    ## Display the first 5 rows
    print("\nFirst 5 rows:")
    print(df.head())

    ## Basic statistics
    print("\nSummary statistics for numeric columns:")
    print(df.describe())

    ## Group by analysis
    print("\nTransaction counts by status:")
    status_counts = df['status'].value_counts()
    print(status_counts)

    ## Calculate average amount by status
    print("\nAverage transaction amount by status:")
    avg_by_status = df.groupby('status')['amount'].mean()
    print(avg_by_status)

    ## Calculate total amount by date (first 5 dates)
    print("\nTotal transaction amount by date (first 5 dates):")
    total_by_date = df.groupby('date')['amount'].sum().sort_values(ascending=False).head(5)
    print(total_by_date)

    end_time = time.time()
    print(f"\nProcessed in {end_time - start_time:.2f} seconds")

## Process the transactions file
process_with_pandas('transactions.csv')

Führen Sie dieses Skript aus:

python3 pandas_basic.py

Erwartete Ausgabe (gekürzt für die Kürze):

Processing transactions.csv with pandas...

DataFrame Info:
Shape: (50000, 6) (rows, columns)
Column names: transaction_id, date, customer_id, product_id, amount, status

First 5 rows:
  transaction_id        date customer_id product_id   amount    status
0    TXN-00000001  2022-12-19   CUST-5421   PROD-383  385.75  refunded
1    TXN-00000002  2022-02-01   CUST-7078   PROD-442  286.83  completed
2    TXN-00000003  2022-12-24   CUST-2356   PROD-701  364.87    failed
3    TXN-00000004  2022-04-09   CUST-3458   PROD-854  247.73   pending
4    TXN-00000005  2022-03-07   CUST-6977   PROD-307  298.69  completed

Summary statistics for numeric columns:
              amount
count  50000.000000
mean     254.797067
std      141.389125
min       10.010000
25%      127.732500
50%      254.865000
75%      381.387500
max      499.990000

Transaction counts by status:
pending     12598
refunded    12556
completed   12432
failed      12414
Name: status, dtype: int64

Average transaction amount by status:
status
completed    255.028733
failed       254.709444
pending      254.690785
refunded     254.760390
Name: amount, dtype: float64

Total transaction amount by date (first 5 dates):
date
2022-01-20    38883.19
2022-08-30    38542.49
2022-03-10    38331.67
2022-11-29    38103.61
2022-06-24    37954.87
Name: amount, dtype: float64

Processed in 0.11 seconds

Verarbeitung großer CSV-Dateien mit Pandas

Obwohl Pandas leistungsstark ist, kann es beim Laden großer CSV-Dateien viel Speicher verbrauchen. Für sehr große Dateien können Sie den Parameter chunksize verwenden, um die Datei in Blöcken zu lesen. Erstellen Sie eine Datei mit dem Namen pandas_chunked.py:

import pandas as pd
import time

def process_large_csv_with_pandas(filename, chunk_size=10000):
    print(f"Processing {filename} with pandas using chunks of {chunk_size} rows...")
    start_time = time.time()

    ## Initialize variables to store aggregated results
    total_rows = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}
    daily_totals = {}

    ## Process the file in chunks
    for chunk_num, chunk in enumerate(pd.read_csv(filename, chunksize=chunk_size)):
        ## Update row count
        chunk_rows = len(chunk)
        total_rows += chunk_rows

        ## Update total amount
        chunk_amount = chunk['amount'].sum()
        total_amount += chunk_amount

        ## Update status counts
        for status, count in chunk['status'].value_counts().items():
            status_counts[status] += count

        ## Update daily totals
        for date, group in chunk.groupby('date'):
            if date in daily_totals:
                daily_totals[date] += group['amount'].sum()
            else:
                daily_totals[date] = group['amount'].sum()

        print(f"Processed chunk {chunk_num + 1} ({total_rows:,} rows so far)")

    ## Calculate results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults after processing {total_rows:,} rows in {processing_time:.2f} seconds:")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/total_rows:.2f}")

    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / total_rows) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

    ## Show top 5 days by transaction amount
    print("\nTop 5 days by transaction amount:")
    top_days = sorted(daily_totals.items(), key=lambda x: x[1], reverse=True)[:5]
    for date, amount in top_days:
        print(f"  {date}: ${amount:,.2f}")

## Process the transactions file with chunks
process_large_csv_with_pandas('transactions.csv', chunk_size=10000)

Führen Sie dieses Skript aus:

python3 pandas_chunked.py

Erwartete Ausgabe:

Processing transactions.csv with pandas using chunks of 10000 rows...
Processed chunk 1 (10,000 rows so far)
Processed chunk 2 (20,000 rows so far)
Processed chunk 3 (30,000 rows so far)
Processed chunk 4 (40,000 rows so far)
Processed chunk 5 (50,000 rows so far)

Results after processing 50,000 rows in 0.34 seconds:
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Top 5 days by transaction amount:
  2022-01-20: $38,883.19
  2022-08-30: $38,542.49
  2022-03-10: $38,331.67
  2022-11-29: $38,103.61
  2022-06-24: $37,954.87

Filtern und Transformieren von Daten mit Pandas

Einer der großen Vorteile von Pandas ist die Möglichkeit, Daten einfach zu filtern und zu transformieren. Erstellen Sie eine Datei mit dem Namen pandas_filter.py:

import pandas as pd
import time

def filter_and_transform(filename):
    print(f"Filtering and transforming data from {filename}...")
    start_time = time.time()

    ## Read the CSV file
    df = pd.read_csv(filename)

    ## 1. Filter: Get only completed transactions
    completed = df[df['status'] == 'completed']
    print(f"Number of completed transactions: {len(completed)}")

    ## 2. Filter: Get high-value transactions (over $400)
    high_value = df[df['amount'] > 400]
    print(f"Number of high-value transactions (>${400}): {len(high_value)}")

    ## 3. Filter: Transactions from first quarter of 2022
    df['date'] = pd.to_datetime(df['date'])  ## Convert to datetime
    q1_2022 = df[(df['date'] >= '2022-01-01') & (df['date'] <= '2022-03-31')]
    print(f"Number of transactions in Q1 2022: {len(q1_2022)}")

    ## 4. Add a new column: transaction_month
    df['month'] = df['date'].dt.strftime('%Y-%m')

    ## 5. Group by month and status
    monthly_by_status = df.groupby(['month', 'status']).agg({
        'transaction_id': 'count',
        'amount': 'sum'
    }).rename(columns={'transaction_id': 'count'})

    ## Calculate success rate by month (completed / total)
    print("\nTransaction success rates by month:")
    for month, month_data in df.groupby('month'):
        total = len(month_data)
        completed = len(month_data[month_data['status'] == 'completed'])
        success_rate = (completed / total) * 100
        print(f"  {month}: {success_rate:.1f}% ({completed}/{total})")

    ## Save filtered data to a new CSV file
    completed_high_value = df[(df['status'] == 'completed') & (df['amount'] > 300)]
    output_file = 'high_value_completed.csv'
    completed_high_value.to_csv(output_file, index=False)

    end_time = time.time()
    print(f"\nFiltering completed in {end_time - start_time:.2f} seconds")
    print(f"Saved {len(completed_high_value)} high-value completed transactions to {output_file}")

## Filter and transform the data
filter_and_transform('transactions.csv')

Führen Sie dieses Skript aus:

python3 pandas_filter.py

Erwartete Ausgabe:

Filtering and transforming data from transactions.csv...
Number of completed transactions: 12432
Number of high-value transactions (>$400): 6190
Number of transactions in Q1 2022: 12348

Transaction success rates by month:
  2022-01: 24.8% (1048/4225)
  2022-02: 25.0% (1010/4034)
  2022-03: 25.4% (1042/4089)
  2022-04: 24.2% (978/4052)
  2022-05: 24.4% (1047/4297)
  2022-06: 24.4% (1046/4280)
  2022-07: 24.7% (1071/4341)
  2022-08: 25.1% (1090/4343)
  2022-09: 26.1% (1091/4177)
  2022-10: 24.1% (1008/4182)
  2022-11: 24.8% (1009/4075)
  2022-12: 25.2% (992/3905)

Filtering completed in 0.38 seconds
Saved 6304 high-value completed transactions to high_value_completed.csv

Vorteile der Verwendung von Pandas

Wie Sie an diesen Beispielen sehen können, bietet Pandas mehrere Vorteile für die CSV-Verarbeitung:

  1. Umfangreiche Funktionalität: Eingebaute Methoden zum Filtern, Gruppieren, Aggregieren und Transformieren von Daten
  2. Leistung: Optimierter C-Code im Hintergrund für schnelle Operationen auf großen Datensätzen
  3. Einfache Datenanalyse: Einfache Möglichkeiten zur Berechnung von Statistiken und zum Gewinnen von Erkenntnissen
  4. Visualisierungsmöglichkeiten: Einfache Integration mit Plotting-Bibliotheken (nicht in den Beispielen gezeigt)
  5. Chunked Processing (Blockweise Verarbeitung): Möglichkeit, Dateien zu verarbeiten, die größer als der verfügbare Speicher sind

Für die meisten Datenanalyseaufgaben mit CSV-Dateien ist Pandas der empfohlene Ansatz, es sei denn, Sie haben spezifische Speicherbeschränkungen, die die Verwendung des reinen Python csv-Moduls erfordern.

Zusammenfassung

In diesem Tutorial haben Sie verschiedene Ansätze zur effizienten Verarbeitung von CSV-Dateien in Python gelernt, von kleinen Datensätzen bis hin zu großen, die ein sorgfältiges Speichermanagement erfordern:

  1. Grundlegende CSV-Verarbeitung: Verwendung des integrierten csv-Moduls von Python zum Lesen und Schreiben von CSV-Dateien mit csv.reader und csv.writer.

  2. Wörterbuchbasierte Verarbeitung: Verwendung von csv.DictReader, um mit CSV-Daten auf intuitivere Weise zu arbeiten und auf Felder nach Namen anstelle von Indizes zuzugreifen.

  3. Effiziente Verarbeitungstechniken:

    • Streaming: Verarbeitung von Dateien Zeile für Zeile für minimalen Speicherverbrauch
    • Chunking (Blockweise Verarbeitung): Verarbeitung von Dateien in Batches für ein besseres Speichermanagement
  4. Erweiterte Verarbeitung mit Pandas:

    • Lesen von CSV-Dateien in DataFrames
    • Analysieren und Filtern von Daten
    • Verarbeiten großer Dateien in Blöcken
    • Transformieren und Exportieren von Daten

Diese Techniken bieten ein umfassendes Toolkit für die Verarbeitung von CSV-Dateien beliebiger Größe in Python. Für die meisten Datenanalyseaufgaben ist Pandas die empfohlene Bibliothek, da sie über eine umfangreiche Funktionalität und Leistung verfügt. Für sehr große Dateien oder einfache Verarbeitungsaufgaben können die Streaming- und Chunking-Ansätze mit dem integrierten csv-Modul jedoch speichereffizienter sein.

Durch die Anwendung der geeigneten Technik basierend auf Ihren spezifischen Anforderungen können Sie CSV-Dateien beliebiger Größe effizient verarbeiten, ohne auf Speicherprobleme zu stoßen.