Работа с данными в формате CSV средствами Python
Основы работы с CSV в Python
CSV (Comma-Separated Values) - распространённый формат для табличных данных. В Python для работы с ним существует встроенный модуль csv, а также сторонние библиотеки, такие как pandas и numpy. Выбор подхода зависит от размера файла, требуемой производительности и сложности операций.
Основное решение - модуль csv (DictReader / DictWriter)
Модуль csv - встроенный и не требует установки. Он оптимален для большинства задач: чтение, запись, преобразование типов, работа с разными разделителями и кавычками.
Чтение CSV с заголовками
import csv
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['Имя'], row['Возраст'])Python удалить столбцы (удаление столбцов из таблицы в python)
Иван 25 Мария 30
Python csv (работа с csv в python)
Пояснение: csv.DictReader автоматически использует первую строку как имена полей. Каждая строка возвращается в виде словаря. Параметр encoding задаёт кодировку файла.
Запись CSV с заголовками
import csv
data = [
{'Имя': 'Иван', 'Возраст': 25},
{'Имя': 'Мария', 'Возраст': 30}
]
with open('output.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['Имя', 'Возраст'])
writer.writeheader()
writer.writerows(data)
Пояснение: newline='' предотвращает появление лишних пустых строк в Windows. Метод writeheader записывает заголовок.
Типичные ошибки и их решения:
- Проблема: Не совпадают имена полей в словаре и fieldnames. Решение - убедиться, что ключи словаря соответствуют переданному списку fieldnames.
- Проблема: Ошибка кодировки при чтении. Решение - указать параметр encoding (например, 'utf-8', 'cp1251').
- Проблема: В CSV используются точки с запятой вместо запятых. Решение - передать delimiter=';' в конструктор reader/writer.
Вариант 1: Pandas для анализа данных
Как прочитать CSV в DataFrame и выполнить базовую статистику?
Цель: Когда требуется фильтрация, группировка, объединение таблиц, визуализация. Pandas предоставляет высокоуровневый интерфейс.
import pandas as pd
df = pd.read_csv('data.csv')
print(df.head()) # первые 5 строк
print(df.describe()) # статистика по числовым колонкам
Имя Возраст Зарплата
0 Иван 25 50000
1 Мария 30 60000
Возраст Зарплата
count 2.000000 2.000000
mean 27.500000 55000.000000
std 3.535534 7071.067812
Пояснение: Pandas автоматически определяет типы данных, обрабатывает пропуски (NaN). Для больших файлов можно читать по кускам: chunksize.
Распространённые проблемы:
- Неверное определение разделителя. Решение - указать sep=';' или sep='\t'.
- Пропущенные значения обрабатываются как NaN, что может повлиять на расчёты. Для замены используется fillna().
Вариант 2: NumPy для числовых данных
Как загрузить только числовые столбцы из CSV в массив NumPy?
Цель: Эффективная обработка больших числовых массивов (линейная алгебра, машинное обучение без pandas).
import numpy as np
data = np.loadtxt('numbers.csv', delimiter=',', skiprows=1)
print(data[:3])
[[25. 50000.] [30. 60000.] [22. 45000.]]
Пояснение: skiprows=1 пропускает заголовок. Все данные должны быть числовыми - иначе возникнет ошибка. Для смешанных типов лучше использовать genfromtxt.
Если в файле есть текстовые колонки, loadtxt вызовет ошибку. Решение - использовать np.genfromtxt с параметром dtype=None.
Вариант 3: Ручной split (для простых файлов без кавычек)
Как самый быстрый способ прочитать CSV без зависимостей?
Цель: Минимальная обработка, если файл гарантированно не содержит кавычек и сложных экранирований.
with open('simple.csv', 'r', encoding='utf-8') as f:
lines = f.readlines()
header = lines[0].strip().split(',')
for line in lines[1:]:
row = line.strip().split(',')
print(dict(zip(header, row)))
{'Имя': 'Иван', 'Возраст': '25'}
{'Имя': 'Мария', 'Возраст': '30'}
Пояснение: Метод split не учитывает кавычки, поэтому если значение содержит запятую, возникнет ошибка. Применим только для простых случаев.
Главная проблема - некорректное разбиение при наличии кавычек. Для надёжности лучше использовать модуль csv.
Вариант 4: Генератор для чтения больших файлов
Как обработать CSV размером несколько гигабайт, не загружая его целиком в память?
Цель: Поточная обработка строк (фильтрация, сумма, запись результатов). Экономия памяти.
import csv
def read_large_csv(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
header = next(reader)
yield header
for row in reader:
yield row
for row in read_large_csv('huge.csv'):
# обработка строки
pass
Пояснение: Использование yield превращает функцию в генератор. Каждая строка обрабатывается и отбрасывается, что позволяет работать с файлами любого размера.
Ошибка при работе с памятью возникает, если накапливать много данных в списке. Нужно сразу обрабатывать каждую строку.
Вариант 5: Определение формата с помощью Sniffer
Как автоматически определить разделитель, символ кавычек и диалект CSV?
Цель: Если структура CSV неизвестна или может варьироваться (разные разделители, кодировки).
import csv
with open('unknown.csv', newline='', encoding='utf-8') as f:
sample = f.read(1024) # читаем первые байты для анализа
dialect = csv.Sniffer().sniff(sample)
f.seek(0)
reader = csv.reader(f, dialect)
for row in reader:
print(row)
Пояснение: Sniffer анализирует образец и возвращает объект диалекта с параметрами delimiter, quotechar и др. Полезно для универсальных загрузчиков.
Если образец слишком мал (менее 100 байт), Sniffer может ошибиться. Рекомендуется увеличить размер образца.
Вариант 6: Работа с кодировками и ошибками
Как обработать CSV, где встречаются разные кодировки (например, Windows-1251) или повреждённые байты?
Цель: Извлечение данных из файлов с неизвестной кодировкой или бинарными включениями.
import csv
with open('mixed_encoding.csv', 'r', encoding='utf-8', errors='replace') as f:
reader = csv.reader(f)
for row in reader:
print(row)
Пояснение: Параметр errors='replace' заменяет нечитаемые символы на �, что позволяет продолжить обработку. Для точного преобразования можно использовать модуль chardet для автоматического определения кодировки.
Замена символов может исказить данные. Если важно сохранить точность, лучше определить правильную кодировку и перекодировать файл.
Расширенные примеры работы с CSV
1. Чтение CSV из строки (StringIO)
Иногда данные CSV приходят в виде текстовой строки (например, из HTTP-запроса). Для имитации файлового объекта используется io.StringIO.
import csv
from io import StringIO
csv_text = "Имя,Возраст\nИван,25\nМария,30"
f = StringIO(csv_text)
reader = csv.DictReader(f)
for row in reader:
print(row)
{'Имя': 'Иван', 'Возраст': '25'}
{'Имя': 'Мария', 'Возраст': '30'}
2. Запись CSV с указанием порядка столбцов через DictWriter
Порядок столбцов определяется списком fieldnames. Если словарь содержит лишние ключи, они будут проигнорированы (если не задан extrasaction).
import csv
data = [{'Имя': 'Анна', 'Возраст': 28, 'Город': 'Москва'}]
with open('ordered.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['Возраст', 'Имя'], extrasaction='ignore')
writer.writeheader()
writer.writerows(data)
Содержимое файла ordered.csv: Возраст,Имя 28,Анна
3. Фильтрация строк с помощью генератора
Можно построчно отфильтровать данные, не загружая весь файл.
import csv
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
filtered = (row for row in reader if int(row['Возраст']) > 25)
for person in filtered:
print(person['Имя'])
Мария
4. Пропуск строк с комментариями
Если CSV содержит строки, начинающиеся с символа комментария (например, '#'), их можно проигнорировать.
import csv
with open('comments.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(line for line in f if not line.startswith('#'))
for row in reader:
print(row)
Файл comments.csv:
# Это комментарий Имя,Возраст Иван,25
5. Чтение CSV с разными разделителями и автоматическим определением
Пример с использованием csv.Sniffer для файла с разделителем табуляции.
import csv
with open('tab_sep.csv', newline='', encoding='utf-8') as f:
sample = f.read(2048)
dialect = csv.Sniffer().sniff(sample)
f.seek(0)
reader = csv.reader(f, dialect)
for row in reader:
print(row)
['Имя', 'Возраст', 'Зарплата'] ['Иван', '25', '50000']
6. Обработка больших файлов по кускам (chunks) с pandas
Pandas позволяет читать CSV частями и агрегировать результат.
import pandas as pd
chunksize = 1000
results = []
for chunk in pd.read_csv('huge.csv', chunksize=chunksize):
# пример: средний возраст по каждому куску
mean_age = chunk['Возраст'].mean()
results.append(mean_age)
overall_mean = sum(results) / len(results)
print(f"Средний возраст: {overall_mean:.2f}")
Средний возраст: 34.78
7. Обработка ошибок кодировки с errors='replace'
Когда файл содержит символы не в UTF-8, можно заменить их на знак замены.
import csv
with open('broken_encoding.csv', 'r', encoding='utf-8', errors='replace') as f:
reader = csv.reader(f)
for row in reader:
print(row)
8. Преобразование строк в даты при чтении
Использование параметра parse_dates в pandas или ручной конвертации с datetime.strptime.
import csv
from datetime import datetime
with open('dates.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
date = datetime.strptime(row['Дата'], '%Y-%m-%d')
print(date.year)
9. Объединение нескольких CSV-файлов в один
Построчная запись всех данных из нескольких файлов в один результирующий.
import csv
input_files = ['part1.csv', 'part2.csv']
with open('merged.csv', 'w', newline='', encoding='utf-8') as out:
writer = csv.writer(out)
for i, fname in enumerate(input_files):
with open(fname, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
if i == 0:
writer.writerow(next(reader)) # заголовок только из первого
else:
next(reader) # пропустить заголовки остальных
for row in reader:
writer.writerow(row)
10. Сортировка CSV по столбцу без pandas
Загрузка всех строк, сортировка и запись обратно. Подходит для файлов, помещающихся в память.
import csv
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
rows = list(reader)
rows.sort(key=lambda x: int(x['Возраст']))
with open('sorted.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=reader.fieldnames)
writer.writeheader()
writer.writerows(rows)
11. Агрегация данных (сумма, среднее) по группам
Группировка по одному столбцу и вычисление агрегатов для другого с помощью словарей.
import csv
from collections import defaultdict
with open('sales.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
totals = defaultdict(float)
counts = defaultdict(int)
for row in reader:
product = row['Продукт']
price = float(row['Цена'])
totals[product] += price
counts[product] += 1
for product, total in totals.items():
avg = total / counts[product]
print(f"{product}: средняя цена {avg:.2f}")
12. Создание собственного диалекта с помощью csv.Dialect
Можно определить кастомный формат, если файл использует нестандартные параметры (например, двойные кавычки и обратную косую черту для экранирования).
import csv
class CustomDialect(csv.Dialect):
delimiter = '|'
quotechar = '"'
doublequote = True
skipinitialspace = True
lineterminator = '\n'
quoting = csv.QUOTE_ALL
with open('custom.csv', 'r', newline='', encoding='utf-8') as f:
reader = csv.reader(f, dialect=CustomDialect)
for row in reader:
print(row)
13. Работа с вложенными кавычками и экранированием
Модуль csv корректно обрабатывает поля, содержащие символы-разделители внутри кавычек, а также удвоенные кавычки для экранирования.
import csv
csv_text = '"Иван","25","город Москва, Россия"'
f = StringIO(csv_text)
reader = csv.reader(f)
for row in reader:
print(row)
['Иван', '25', 'город Москва, Россия']
14. Потоковая обработка с записью результата в другой CSV
Чтение из одного файла, преобразование данных и запись в другой, не накапливая строки.
import csv
with open('source.csv', 'r', encoding='utf-8') as src, \
open('result.csv', 'w', newline='', encoding='utf-8') as dst:
reader = csv.DictReader(src)
writer = csv.DictWriter(dst, fieldnames=reader.fieldnames)
writer.writeheader()
for row in reader:
row['Возраст'] = int(row['Возраст']) + 1 # увеличим возраст на 1
writer.writerow(row)