Как эффективно обрабатывать большие CSV-файлы в Python

PythonBeginner
Практиковаться сейчас

Введение

Обработка больших CSV-файлов — распространенная задача для Python-разработчиков. Этот учебник проведет вас через эффективные методы обработки этих файлов, уделяя особое внимание оптимизации производительности и использования памяти. К концу этой лабораторной работы вы будете обладать практическими знаниями для эффективного решения задач обработки CSV-данных, требующих больших объемов данных, в Python.

Независимо от того, анализируете ли вы данные о клиентах, обрабатываете финансовые записи или работаете с любым типом структурированных данных, навыки, полученные в этой лабораторной работе, помогут вам эффективно обрабатывать большие наборы данных, не сталкиваясь с проблемами памяти.

Создание и чтение простого CSV-файла

CSV (Comma-Separated Values) — популярный формат файлов, используемый для хранения табличных данных. На этом шаге мы создадим простой CSV-файл и узнаем, как его читать, используя встроенный модуль csv в Python.

Понимание CSV-файлов

CSV-файл хранит данные в формате обычного текста, где:

  • Каждая строка представляет собой строку данных
  • Значения в каждой строке разделены разделителем (обычно запятой)
  • Первая строка часто содержит заголовки столбцов

Давайте начнем с создания простого CSV-файла для работы.

Создание CSV-файла

Сначала создадим каталог для работы, а затем создадим простой CSV-файл:

  1. Откройте терминал в WebIDE
  2. Создайте новый файл Python с именем csv_basics.py в редакторе

Теперь добавьте следующий код в csv_basics.py:

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.")

Запустите этот код, выполнив следующую команду в терминале:

python3 csv_basics.py

Ожидаемый вывод:

CSV file 'sample_data.csv' created successfully.

Это создаст новый CSV-файл с именем sample_data.csv в вашем текущем каталоге. Вы можете просмотреть содержимое этого файла, запустив:

cat 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

Чтение CSV-файла

Теперь давайте прочитаем CSV-файл, который мы только что создали. Создайте новый файл с именем read_csv.py со следующим кодом:

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}")

Запустите этот код с помощью:

python3 read_csv.py

Ожидаемый вывод:

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

Понимание модуля CSV

Модуль csv в Python предоставляет два основных класса:

  • csv.reader: Читает CSV-файлы и возвращает каждую строку в виде списка строк
  • csv.writer: Записывает данные в CSV-файлы

Этот модуль обрабатывает все сложности, связанные с различными форматами CSV, такие как экранирование специальных символов и обработка кавычек.

На этом шаге вы узнали, как создать и прочитать простой CSV-файл. На следующем шаге мы рассмотрим более эффективные способы обработки больших CSV-файлов.

Использование DictReader для удобной обработки CSV

На предыдущем шаге мы работали с основными функциями csv.reader и csv.writer. Теперь давайте рассмотрим более удобный способ обработки CSV-файлов с использованием класса csv.DictReader, который особенно полезен при работе с данными, имеющими заголовки столбцов.

Что такое DictReader?

csv.DictReader читает CSV-файлы и возвращает каждую строку в виде словаря, где:

  • Ключи берутся из заголовков столбцов (по умолчанию первая строка CSV-файла)
  • Значения — это соответствующие данные из каждой строки

Этот подход делает ваш код более читаемым и менее подверженным ошибкам, потому что вы можете ссылаться на столбцы по имени, а не по индексу.

Создание большего тестового файла

Сначала давайте создадим немного больший CSV-файл, чтобы продемонстрировать преимущества DictReader. Создайте новый файл с именем create_users_data.py со следующим кодом:

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")

Запустите скрипт, чтобы создать файл:

python3 create_users_data.py

Ожидаемый вывод:

Created 'users_data.csv' with 100 user records

Давайте рассмотрим первые несколько строк этого нового файла:

head -n 5 users_data.csv

Вы должны увидеть строку заголовка, за которой следуют 4 строки данных:

id,name,email,age,country
1,John Smith,john.smith@gmail.com,25,USA
2,Emily Brown,emily.brown@yahoo.com,32,Canada
3,David Jones,david.jones@outlook.com,45,UK
4,Sarah Wilson,sarah.wilson@example.com,28,Australia

Использование DictReader для обработки CSV-файла

Теперь давайте создадим скрипт для обработки этого файла с использованием DictReader. Создайте новый файл с именем dict_reader_example.py со следующим кодом:

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")

Запустите этот скрипт:

python3 dict_reader_example.py

Ожидаемый вывод (ваши точные значения могут отличаться, так как данные генерируются случайным образом):

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

First 5 records:
-----------------
User 1: John Smith, 25 years old, from USA
  Email: john.smith@gmail.com
User 2: Emily Brown, 32 years old, from Canada
  Email: emily.brown@yahoo.com
User 3: David Jones, 45 years old, from UK
  Email: david.jones@outlook.com
User 4: Sarah Wilson, 28 years old, from Australia
  Email: sarah.wilson@example.com
User 5: Michael Taylor, 37 years old, from Germany
  Email: michael.taylor@example.com

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

Преимущества использования DictReader

Как видите, использование DictReader предоставляет несколько преимуществ:

  1. Читаемый код: Вы можете получить доступ к полям по имени, не запоминая позиции индексов.
  2. Самодокументирование: Код четко показывает, к какому полю вы обращаетесь.
  3. Гибкость: Если порядок столбцов в CSV-файле изменится, ваш код все равно будет работать, если имена столбцов останутся прежними.

Этот подход особенно полезен при работе с реальными данными, которые имеют много столбцов, или когда порядок столбцов может меняться со временем.

На следующем шаге мы рассмотрим эффективные методы обработки больших CSV-файлов, не загружая все в память одновременно.

Эффективная обработка больших CSV-файлов

В реальных сценариях вам может потребоваться обработать CSV-файлы размером в несколько гигабайт. Загрузка таких файлов полностью в память может привести к сбою вашего приложения или значительному замедлению его работы. На этом шаге мы рассмотрим методы эффективной обработки больших CSV-файлов.

Проблема с памятью при работе с большими файлами

При работе с CSV-файлами существует три распространенных подхода, каждый из которых имеет разные требования к памяти:

  1. Загрузка всего файла в память — просто, но использует больше всего памяти
  2. Потоковая передача файла строка за строкой — использует минимальную память, но может быть медленнее для сложных операций
  3. Разбиение на части (Chunking) — промежуточный вариант, который обрабатывает файл управляемыми частями

Давайте рассмотрим каждый из этих подходов на практических примерах.

Создание большего файла-образца

Сначала давайте создадим больший файл-образец, чтобы продемонстрировать эти методы. Создайте новый файл с именем 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.")

Запустите этот скрипт, чтобы создать файл:

python3 create_large_csv.py

Ожидаемый вывод:

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

Вы можете проверить размер файла с помощью:

ls -lh transactions.csv

Ожидаемый вывод (размер может незначительно отличаться):

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

Подход 1: Построчная обработка (потоковая передача)

Наиболее эффективным с точки зрения памяти подходом является построчная обработка CSV-файла. Создайте файл с именем 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')

Запустите этот скрипт:

python3 streaming_example.py

Ожидаемый вывод (ваши точные числа могут отличаться):

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%)

Подход 2: Обработка по частям (Chunked Processing)

Для более сложных операций или когда вам нужно обрабатывать данные пакетами, вы можете использовать подход с разбивкой на части. Создайте файл с именем 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)

Запустите этот скрипт:

python3 chunked_example.py

Ожидаемый вывод:

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%)

Сравнение использования памяти

Основные различия между этими подходами заключаются в следующем:

  1. Потоковая передача (строка за строкой):

    • Использует минимальную память независимо от размера файла
    • Лучше всего подходит для очень больших файлов
    • Простые операции над каждой строкой
  2. Обработка по частям:

    • Использует больше памяти, чем потоковая передача, но все равно эффективна
    • Подходит для операций, которым необходимо обрабатывать строки пакетами
    • Предоставляет обновления о ходе выполнения во время обработки
    • Может быть объединена с многопроцессорностью для параллельной обработки

Для большинства практических целей рекомендуется подход с потоковой передачей, если только вам специально не нужны возможности пакетной обработки. Он обеспечивает наилучшую эффективность использования памяти, сохраняя при этом хорошую производительность.

На следующем шаге мы рассмотрим использование сторонних библиотек, таких как pandas, для еще более мощных возможностей обработки CSV.

Использование Pandas для расширенной обработки CSV

Хотя встроенный модуль Python csv является мощным, библиотека pandas предлагает более продвинутые функции для анализа и обработки данных. На этом шаге мы рассмотрим, как использовать pandas для эффективной обработки CSV.

Установка Pandas

Сначала давайте установим библиотеку pandas:

pip install pandas

Ожидаемый вывод:

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

Чтение CSV-файлов с помощью Pandas

Pandas упрощает чтение, анализ и обработку данных CSV. Создайте файл с именем 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')

Запустите этот скрипт:

python3 pandas_basic.py

Ожидаемый вывод (усечено для краткости):

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

Обработка больших CSV-файлов с помощью Pandas

Хотя pandas является мощным инструментом, он может потреблять много памяти при загрузке больших CSV-файлов. Для очень больших файлов вы можете использовать параметр chunksize, чтобы читать файл частями. Создайте файл с именем 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)

Запустите этот скрипт:

python3 pandas_chunked.py

Ожидаемый вывод:

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

Фильтрация и преобразование данных с помощью Pandas

Одним из больших преимуществ pandas является возможность легкой фильтрации и преобразования данных. Создайте файл с именем 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')

Запустите этот скрипт:

python3 pandas_filter.py

Ожидаемый вывод:

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

Преимущества использования Pandas

Как вы можете видеть из этих примеров, pandas предлагает несколько преимуществ для обработки CSV:

  1. Богатая функциональность: Встроенные методы для фильтрации, группировки, агрегирования и преобразования данных
  2. Производительность: Оптимизированный код на C для быстрых операций над большими наборами данных
  3. Простой анализ данных: Простые способы расчета статистики и получения информации
  4. Возможности визуализации: Легкая интеграция с библиотеками построения графиков (не показано в примерах)
  5. Обработка по частям: Возможность обработки файлов, размер которых превышает доступную память

Для большинства задач анализа данных, связанных с CSV-файлами, pandas является рекомендуемым подходом, если у вас нет конкретных ограничений по памяти, требующих использования чистого модуля Python csv.

Резюме

В этом руководстве вы изучили несколько подходов к эффективной обработке CSV-файлов в Python, от небольших наборов данных до больших, требующих тщательного управления памятью:

  1. Базовая обработка CSV: Использование встроенного модуля Python csv для чтения и записи CSV-файлов с помощью csv.reader и csv.writer.

  2. Обработка на основе словарей: Использование csv.DictReader для работы с данными CSV более интуитивным способом, доступ к полям по имени, а не по индексу.

  3. Эффективные методы обработки:

    • Потоковая передача (Streaming): Обработка файлов строка за строкой для минимального использования памяти
    • Разбиение на части (Chunking): Обработка файлов пакетами для лучшего управления памятью
  4. Расширенная обработка с помощью Pandas:

    • Чтение CSV-файлов в DataFrames
    • Анализ и фильтрация данных
    • Обработка больших файлов частями
    • Преобразование и экспорт данных

Эти методы предоставляют комплексный инструментарий для обработки CSV-файлов любого размера в Python. Для большинства задач анализа данных pandas является рекомендуемой библиотекой из-за ее богатой функциональности и производительности. Однако для очень больших файлов или простых задач обработки подходы с потоковой передачей и разбивкой на части со встроенным модулем csv могут быть более эффективными с точки зрения памяти.

Применяя соответствующий метод в зависимости от ваших конкретных требований, вы можете эффективно обрабатывать CSV-файлы любого размера, не сталкиваясь с проблемами с памятью.