pandas DataFrame: чтение CSV, loc и iloc на примерах

опубликовано
читать
≈ 9 мин
автор
среда и версия
pandas 3.0.2 в двух средах: Pyodide 314.0.3 с Python 3.14.2, на котором работает песочница, и CPython 3.14.8. Вывод совпал, два расхождения отмечены в тексте
содержание решать задачи Продуктовая аналитика «Пульс»

DataFrame — таблица pandas: строки подписаны метками индекса, а у каждого столбца свой тип. Ниже — заказы магазина в pandas 3.0 с живым выводом: чтение CSV и файла из Excel, размер и типы, выбор строк через loc и iloc, фильтры, новый столбец, сохранение и ошибки. Все примеры можно повторить в песочнице Python: pandas скачается сам при первом import.

Python / 01

loc ищет по метке, iloc — по позиции

loc ищет по метке, iloc — по позицииТаблица by_amount — заказы, отсортированные по сумме. Позиции строк от 0 до 5, метки индекса 2, 5, 0, 3, 1, 4. by_amount.iloc[0] берёт строку на позиции 0 — Вера, 3490. by_amount.loc[0] берёт строку с меткой 0 — Анна, 1290, она стоит третьей. Срез orders.loc[1:3] включает метку 3 и возвращает три строки, orders.iloc[1:3] не включает позицию 3 и возвращает две.позицияметкаcustomeramount02Вера3490.015Дина2150.020Анна1290.033Анна780.041Борис560.054ГлебNaNby_amount.iloc[0]позиция 0 — первая строка: Вераby_amount.loc[0]метка 0 — третья строка: Аннасрезыorders.loc[1:3]метки 1, 2 и 3 — конец включён: 3 строкиorders.iloc[1:3]позиции 1 и 2 — конец не включён: 2 строки
После сортировки метки индекса не совпадают с позициями. loc[0] найдёт строку с меткой 0, где бы она ни стояла, а iloc[0] возьмёт первую строку таблицы. Срез loc включает правую границу, срез iloc — нет.

Создать DataFrame

Проще всего собрать таблицу из словаря: ключи станут названиями столбцов, а списки — их значениями.

import pandas as pd

df = pd.DataFrame({
    'customer': ['Анна', 'Борис', 'Вера'],
    'amount': [1290, 560, 3490],
})
print(df)
  customer  amount
0     Анна    1290
1    Борис     560
2     Вера    3490

Числа слева pandas добавил сам: это индекс, метки строк. Пока они совпадают с номерами строк, но дальше станет видно, что это разные вещи.

Прочитать CSV

Обычно данные приходят файлом. Положи рядом с программой orders.csv:

order_id,date,customer,city,amount,status
1001,2026-09-01,Анна,Москва,1290,paid
1002,2026-09-01,Борис,Казань,560,paid
1003,2026-09-02,Вера,Москва,3490,cancelled
1004,2026-09-03,Анна,Москва,780,paid
1005,2026-09-03,Глеб,Пермь,,new
1006,2026-09-04,Дина,Казань,2150,paid

read_csv читает его в DataFrame, первая строка файла становится заголовками столбцов:

import pandas as pd

orders = pd.read_csv('orders.csv')
print(orders)
   order_id        date customer    city  amount     status
0      1001  2026-09-01     Анна  Москва  1290.0       paid
1      1002  2026-09-01    Борис  Казань   560.0       paid
2      1003  2026-09-02     Вера  Москва  3490.0  cancelled
3      1004  2026-09-03     Анна  Москва   780.0       paid
4      1005  2026-09-03     Глеб   Пермь     NaN        new
5      1006  2026-09-04     Дина  Казань  2150.0       paid

Пустая ячейка в заказе Глеба стала NaN, а все суммы — дробными. Целочисленный столбец не может хранить пропуск, поэтому pandas дал всему столбцу тип float64. Если таблица большая, первые строки показывает orders.head(): пять по умолчанию, head(3) — три.

Сколько строк и какие типы

print(orders.shape)
print(len(orders))
print(orders.columns.tolist())
print(orders.dtypes)
(6, 6)
6
['order_id', 'date', 'customer', 'city', 'amount', 'status']
order_id      int64
date            str
customer        str
city            str
amount      float64
status          str
dtype: object

shape — пара «строки, столбцы», len — число строк. Строковые столбцы в pandas 3 получают тип str. В pandas 2 и в большинстве старых материалов на этом месте стоит object.

Дата пока тоже строка: сравнить её с другой датой или достать день недели не выйдет. parse_dates превращает столбец в даты прямо при чтении:

orders = pd.read_csv('orders.csv', parse_dates=['date'])
print(orders.dtypes)
print(orders['date'].dt.day.tolist())
order_id             int64
date        datetime64[us]
customer               str
city                   str
amount             float64
status                 str
dtype: object
[1, 1, 2, 3, 3, 4]

Дальше в примерах orders прочитан именно так. Сводку по таблице одной командой даёт info(): сколько непустых значений в каждом столбце и какого они типа.

orders.info()
<class 'pandas.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 6 columns):
 #   Column    Non-Null Count  Dtype         
---  ------    --------------  -----         
 0   order_id  6 non-null      int64         
 1   date      6 non-null      datetime64[us]
 2   customer  6 non-null      str           
 3   city      6 non-null      str           
 4   amount    5 non-null      float64       
 5   status    6 non-null      str           
dtypes: datetime64[us](1), float64(1), int64(1), str(3)
memory usage: 288.0 bytes

В amount пять непустых значений из шести — это пропуск Глеба. Последняя строка зависит от платформы: в песочнице 288 байт, в CPython 3.14 на той же таблице — 420.

Выбрать столбцы

Один столбец в квадратных скобках — это Series, одномерный столбец с тем же индексом. Список столбцов в двойных скобках возвращает DataFrame:

print(orders['amount'])
print(type(orders['amount']).__name__)
print(orders[['customer', 'amount']])
0    1290.0
1     560.0
2    3490.0
3     780.0
4       NaN
5    2150.0
Name: amount, dtype: float64
Series
  customer  amount
0     Анна  1290.0
1    Борис   560.0
2     Вера  3490.0
3     Анна   780.0
4     Глеб     NaN
5     Дина  2150.0

loc и iloc: метка против позиции

loc выбирает строки по меткам индекса, iloc — по позиции в таблице, считая с нуля. Пока индекс идёт по порядку, разницы не видно. Отсортируем заказы по сумме:

by_amount = orders.sort_values('amount', ascending=False)
print(by_amount[['customer', 'amount']])
print(by_amount.loc[0, 'customer'])
print(by_amount['customer'].iloc[0])
  customer  amount
2     Вера  3490.0
5     Дина  2150.0
0     Анна  1290.0
3     Анна   780.0
1    Борис   560.0
4     Глеб     NaN
Анна
Вера

Строки переехали вместе со своими метками. loc[0] нашёл строку с меткой 0 — заказ Анны, хотя он теперь третий. iloc[0] взял первую строку таблицы — заказ Веры. Строка без суммы при сортировке уходит в конец.

У срезов разные границы. Срез loc включает обе метки, срез iloc устроен как обычный срез Python и последнюю позицию не берёт:

print(orders.loc[1:3])
print(orders.iloc[1:3])
   order_id       date customer    city  amount     status
1      1002 2026-09-01    Борис  Казань   560.0       paid
2      1003 2026-09-02     Вера  Москва  3490.0  cancelled
3      1004 2026-09-03     Анна  Москва   780.0       paid
   order_id       date customer    city  amount     status
1      1002 2026-09-01    Борис  Казань   560.0       paid
2      1003 2026-09-02     Вера  Москва  3490.0  cancelled

Главная мысль: loc спрашивает «какая у строки метка», iloc — «где строка стоит». После сортировки или фильтра это разные строки, поэтому номер по порядку бери через iloc, а конкретную запись — через loc.

Фильтр строк по условию

Сравнение со столбцом даёт столбец из True и False. Если подставить его в квадратные скобки, останутся строки с True:

paid = orders[orders['status'] == 'paid']
print(paid)
   order_id       date customer    city  amount status
0      1001 2026-09-01     Анна  Москва  1290.0   paid
1      1002 2026-09-01    Борис  Казань   560.0   paid
3      1004 2026-09-03     Анна  Москва   780.0   paid
5      1006 2026-09-04     Дина  Казань  2150.0   paid

Несколько условий соединяют через & (и), | (или) и ~ (не), каждое — в своих скобках. Через запятую в loc можно сразу выбрать нужные столбцы, а isin проверяет попадание в список:

moscow = orders.loc[(orders['city'] == 'Москва') & (orders['amount'] > 1000), ['customer', 'amount']]
print(moscow)
print(orders[orders['city'].isin(['Казань', 'Пермь'])])
  customer  amount
0     Анна  1290.0
2     Вера  3490.0
   order_id       date customer    city  amount status
1      1002 2026-09-01    Борис  Казань   560.0   paid
4      1005 2026-09-03     Глеб   Пермь     NaN    new
5      1006 2026-09-04     Дина  Казань  2150.0   paid

Слова and и or вместо & и | здесь не работают. Почему — в разделе об ошибках.

Добавить и удалить столбец, добавить строку

Новый столбец получают присваиванием: pandas посчитает выражение для всех строк сразу.

orders['is_big'] = orders['amount'] >= 1000
print(orders[['customer', 'amount', 'is_big']])
  customer  amount  is_big
0     Анна  1290.0    True
1    Борис   560.0   False
2     Вера  3490.0    True
3     Анна   780.0   False
4     Глеб     NaN   False
5     Дина  2150.0    True

Сравнение с NaN всегда даёт False, поэтому заказ без суммы попал в «небольшие». Если пропуски важны, проверяй их отдельно через isna().

Удаляет столбец drop. Признак больше не нужен — уберём его, и в таблице снова шесть столбцов:

orders = orders.drop(columns='is_big')
print(orders.columns.tolist())
['order_id', 'date', 'customer', 'city', 'amount', 'status']

rename переименовывает столбцы по словарю «старое имя — новое» и возвращает новую таблицу:

renamed = orders.rename(columns={'amount': 'total', 'customer': 'client'})
print(renamed.columns.tolist())
['order_id', 'date', 'client', 'city', 'total', 'status']

Строку добавляют через pd.concat: новую запись оформляют как DataFrame из одной строки и склеивают с таблицей. ignore_index=True раздаёт строкам метки заново, по порядку:

row = pd.DataFrame([{'order_id': 1007, 'date': pd.Timestamp('2026-09-05'), 'customer': 'Ева', 'city': 'Пермь', 'amount': 990.0, 'status': 'new'}])
orders = pd.concat([orders, row], ignore_index=True)
print(orders.tail(2))
   order_id       date customer    city  amount status
5      1006 2026-09-04     Дина  Казань  2150.0   paid
6      1007 2026-09-05      Ева   Пермь   990.0    new

Дату в новой строке стоит передать как pd.Timestamp, а не строкой: тогда столбец date останется датами. Метод append, который встречается в старых примерах, удалён в pandas 2.0.

Сохранить в CSV

paid = orders[orders['status'] == 'paid']
paid.to_csv('paid.csv', index=False)
print(open('paid.csv', encoding='utf-8').read())
order_id,date,customer,city,amount,status
1001,2026-09-01,Анна,Москва,1290.0,paid
1002,2026-09-01,Борис,Казань,560.0,paid
1004,2026-09-03,Анна,Москва,780.0,paid
1006,2026-09-04,Дина,Казань,2150.0,paid

Без index=False pandas запишет метки индекса первым безымянным столбцом, и при следующем чтении он вернётся как лишний.

CSV из русского Excel

Русский Excel сохраняет обычный CSV по-своему: разделитель — точка с запятой, в дробных числах — запятая, кодировка — Windows-1251. Такой excel.csv выглядит так:

Номер;Дата;Клиент;Сумма
1001;01.09.2026;Анна;1290,50
1002;01.09.2026;Борис;560,00
1003;02.09.2026;Вера;3490,00

Без Excel такой файл можно создать из Python:

text = 'Номер;Дата;Клиент;Сумма\n1001;01.09.2026;Анна;1290,50\n1002;01.09.2026;Борис;560,00\n1003;02.09.2026;Вера;3490,00\n'
with open('excel.csv', 'w', encoding='cp1251') as f:
    f.write(text)

С настройками по умолчанию read_csv ждёт UTF-8 и падает на первом же байте:

print(pd.read_csv('excel.csv'))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcd in position 0: invalid continuation byte

Если указать только кодировку, ошибки не будет, но таблица получится сломанной:

print(pd.read_csv('excel.csv', encoding='cp1251'))
                           Номер;Дата;Клиент;Сумма
1001;01.09.2026;Анна;1290                       50
1002;01.09.2026;Борис;560                        0
1003;02.09.2026;Вера;3490                        0

pandas делит строки по запятой, и запятая нашлась только в суммах. Шапка осталась одним столбцом, а копейки уехали в отдельное поле. Разделитель, кодировку и десятичный знак нужно назвать вместе, а даты в формате «день.месяц.год» разобрать с явным форматом:

excel = pd.read_csv('excel.csv', sep=';', encoding='cp1251', decimal=',')
excel['Дата'] = pd.to_datetime(excel['Дата'], format='%d.%m.%Y')
print(excel)
print(excel.dtypes)
   Номер       Дата Клиент   Сумма
0   1001 2026-09-01   Анна  1290.5
1   1002 2026-09-01  Борис   560.0
2   1003 2026-09-02   Вера  3490.0
Номер              int64
Дата      datetime64[us]
Клиент               str
Сумма            float64
dtype: object

Ошибки, на которых спотыкаются

Ниже — последняя строка трассировки: в ней тип ошибки и её текст.

KeyError с именем столбца. Такого столбца нет: опечатка, другой регистр или пробел, оставшийся в шапке файла.

KeyError: 'Amount'

Сверь имя с orders.columns.tolist().

The truth value of a Series is ambiguous. В условии стоит and или or:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

and требует от каждой стороны одно значение «истина или ложь», а сравнение со столбцом даёт целый столбец. Для столбцов есть &, | и ~.

unsupported operand type(s) for &. Условия соединены через &, но без скобок:

print(orders[orders['amount'] > 1000 & orders['city'] == 'Москва'])
TypeError: unsupported operand type(s) for &: 'int' and 'StringArray'

& выполняется раньше сравнений, и Python сначала считает 1000 & orders['city'] — число и строки. Каждое условие бери в скобки.

ChainedAssignmentError. Присваивание через две пары скобок подряд ничего не меняет:

import pandas as pd

orders = pd.read_csv('orders.csv', parse_dates=['date'])
orders[orders['customer'] == 'Анна']['amount'] = 0
print(orders.loc[orders['customer'] == 'Анна', 'amount'])
0    1290.0
3     780.0
Name: amount, dtype: float64

Суммы остались прежними, а pandas 3 выводит предупреждение. Его первая строка:

main.py:4: ChainedAssignmentError: A value is being set on a copy of a DataFrame or Series through chained assignment.

Фильтр создаёт копию, и значение записывается в неё, а не в orders. Строки и столбец нужно назвать в одном loc:

orders.loc[orders['customer'] == 'Анна', 'amount'] = 0
print(orders.loc[orders['customer'] == 'Анна', 'amount'])
0    0.0
3    0.0
Name: amount, dtype: float64

‘DataFrame’ object has no attribute ‘append’. Код написан для pandas до версии 2.0. Строки теперь добавляют через pd.concat, как в примере выше.

AttributeError: 'DataFrame' object has no attribute 'append'

FileNotFoundError. pandas ищет файл относительно папки, из которой запущена программа, а не той, где лежит скрипт.

FileNotFoundError: [Errno 44] No such file or directory: 'order.csv'

Номер 44 выдаёт песочница, в CPython на Linux, macOS и Windows та же ошибка приходит с номером 2. Проверь имя файла и текущую папку: import os; print(os.getcwd()).

UnicodeDecodeError. Файл не в UTF-8 — чаще всего это CSV из Excel. Решение — в разделе про русский Excel.

No module named ‘pandas’. На компьютере pandas не установлен в тот Python, которым запускается программа:

ModuleNotFoundError: No module named 'pandas'

Поставь его командой python -m pip install pandas тем же интерпретатором, лучше в виртуальном окружении. Если ошибка не уходит, причину поможет найти разбор ModuleNotFoundError. В песочнице устанавливать ничего не нужно.

Частые вопросы

Как узнать количество строк в DataFrame

len(df) возвращает число строк, df.shape — пару «строки, столбцы». У таблицы заказов это 6 и (6, 6).

Чем loc отличается от iloc

loc выбирает по меткам индекса и включает правую границу среза. iloc выбирает по позиции, считая с нуля, и правую границу не включает. После сортировки и фильтров метки и позиции расходятся, как на схеме.

Как вывести названия столбцов

df.columns.tolist() вернёт обычный список имён. Он пригодится, когда KeyError жалуется на столбец, который вроде бы есть.

Как добавить строку в DataFrame

Оформи строку как DataFrame из одной записи и склей с таблицей: pd.concat([df, row], ignore_index=True). Метода append в pandas 2 и 3 нет.

Где потренироваться

В песочнице Python pandas скачивается сам при первом import: создай рядом с main.py файл orders.csv из этой статьи и повтори примеры. Механику воронок, сессий и когорт на чистом Python, без pandas, отрабатывают задачи по продуктовой аналитике.

Источники