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