Перейти к содержанию
Educora
Университет25 мин31 / 42

Основы pandas: Series и DataFrame

Работай с табличными данными в pandas: Series и DataFrame, чтение CSV, `head`, `info`, `describe`, выбор через `loc` и `iloc`, фильтрация, сортировка и новые столбцы.

Проверь себя
В этом уроке ты узнаешь
  • Объяснять устройство Series и DataFrame (значения, индекс, столбцы, типы)
  • Читать CSV и делать первый обзор данных с помощью head, info, describe и value_counts
  • Выбирать и сортировать строки и столбцы через loc, iloc, маски и query
  • Создавать новые столбцы векторизованно и без цепочечного присваивания

Айсель ведёт небольшой интернет-магазин канцтоваров. Продажи у неё в таблице Excel: товар, категория, цена, остаток на складе, продано штук. Чтобы на сотнях строк отвечать на вопросы «какая категория приносит больше выручки?» или «что пора заказать снова?», существует библиотека pandas. Она добавляет к массивам NumPy названия столбцов, метки строк и сотни готовых операций над таблицами.

Series и DataFrame

Определение
Series

Одномерный массив с метками: значения (массив NumPy) и соответствующий им индекс (метки). У него есть имя (name) и тип (dtype).

Определение
DataFrame

Двумерная таблица: набор Series (столбцов) с общим индексом. У каждого столбца может быть свой тип — текст, целое, дробное число, дата.

К элементу Series можно обратиться и по метке (s['Pen']), и по позиции (s.iloc[0]). Арифметика векторизована, как в NumPy. Ниже прибавляем к ценам 18% НДС:

Python
import pandas as pd

prices = pd.Series([1.5, 0.6, 35.0, 49.9],
                   index=['Notebook', 'Pen', 'Backpack', 'Headphones'],
                   name='price')
print(prices)
print(prices['Pen'], prices.iloc[-1])
print(prices[prices > 10].index.tolist())
print((prices * 1.18).round(2).tolist())
▸ Ожидаемый результат
Notebook       1.5
Pen            0.6
Backpack      35.0
Headphones    49.9
Name: price, dtype: float64
0.6 49.9
['Backpack', 'Headphones']
[1.77, 0.71, 41.3, 58.88]

Проще всего построить DataFrame из словаря: ключи — названия столбцов, значения — их содержимое. shape — кортеж (строки, столбцы), а dtypes показывает тип каждого столбца. В pandas 3 текстовые столбцы имеют тип str (в старых версиях увидишь object).

Python
import pandas as pd

df = pd.DataFrame({
    'name': ['Aysel', 'Murad', 'Leyla'],
    'age': [19, 21, 20],
    'city': ['Baku', 'Ganja', 'Baku'],
})
print(df)
print(df.shape, df.columns.tolist())
print(df.dtypes)
▸ Ожидаемый результат
    name  age   city
0  Aysel   19   Baku
1  Murad   21  Ganja
2  Leyla   20   Baku
(3, 3) ['name', 'age', 'city']
name      str
age     int64
city      str
dtype: object

Чтение CSV и первый взгляд на данные

В реальной работе таблицу читают из файла: pd.read_csv('sales.csv'). В браузере файлов нет, поэтому превращаем текст в «файл» с помощью io.StringIO — для read_csv разницы нет. Каждый блок кода выполняется отдельно, поэтому таблицу создаём в каждом блоке заново. Первый шаг всегда одинаков: head() показывает первые строки, а info() — столбцы, их типы и число непустых значений. Ниже выводим те же сведения по частям: shape, dtypes и count().

Python
import io
import pandas as pd

data = '''product,category,price,stock,sold
Notebook,Stationery,1.5,120,340
Pen,Stationery,0.6,300,820
Backpack,Accessories,35.0,25,48
Headphones,Electronics,49.9,15,37
USB cable,Electronics,6.5,80,150
Mug,Home,8.0,40,95
Desk lamp,Home,27.5,12,20'''
df = pd.read_csv(io.StringIO(data))
print(df.head(3))
print(df.shape)
print(df.dtypes)
print(df.count().tolist())
▸ Ожидаемый результат
    product     category  price  stock  sold
0  Notebook   Stationery    1.5    120   340
1       Pen   Stationery    0.6    300   820
2  Backpack  Accessories   35.0     25    48
(7, 5)
product         str
category        str
price       float64
stock         int64
sold          int64
dtype: object
[7, 7, 7, 7, 7]
read_csv сам определил типы: числа — int64 и float64, текст — str. count() считает непустые значения в каждом столбце — пропусков здесь нет.

describe() выдаёт для каждого числового столбца количество, среднее, стандартное отклонение (с делением на n − 1), минимум, квартили и максимум в одной таблице. Для категориального столбца value_counts() считает, сколько раз встречается каждое значение.

Python
import io
import pandas as pd

data = '''product,category,price,stock,sold
Notebook,Stationery,1.5,120,340
Pen,Stationery,0.6,300,820
Backpack,Accessories,35.0,25,48
Headphones,Electronics,49.9,15,37
USB cable,Electronics,6.5,80,150
Mug,Home,8.0,40,95
Desk lamp,Home,27.5,12,20'''
df = pd.read_csv(io.StringIO(data))
print(df.describe().round(2))
print(df['category'].value_counts())
▸ Ожидаемый результат
       price   stock    sold
count   7.00    7.00    7.00
mean   18.43   84.57  215.71
std    19.16  102.74  288.06
min     0.60   12.00   20.00
25%     4.00   20.00   42.50
50%     8.00   40.00   95.00
75%    31.25  100.00  245.00
max    49.90  300.00  820.00
category
Stationery     2
Electronics    2
Home           2
Accessories    1
Name: count, dtype: int64
h = (n − 1) · q Q(q) = x₍ₖ₎ + (h − k) · (x₍ₖ₊₁₎ − x₍ₖ₎), k = ⌊h⌋
где:
  • qуровень: 0,25 (первый квартиль), 0,5 (медиана), 0,75 (третий квартиль)
  • x₍ₖ₎k-е значение после сортировки по возрастанию (счёт с 0)
  • h«дробная позиция» в отсортированных данных

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

Пример 1: проверим квартили из describe

Вычисли вручную квартили 25% и 75% столбца price и сравни с результатом describe().

Показать решение
Отсортированные цены: 0,6; 1,5; 6,5; 8,0; 27,5; 35,0; 49,9 (n = 7).
q = 0,25: h = 6 · 0,25 = 1,5 → середина между x₍₁₎ = 1,5 и x₍₂₎ = 6,5: 1,5 + 0,5 · 5 = 4,0.
q = 0,75: h = 6 · 0,75 = 4,5 → 27,5 + 0,5 · 7,5 = 31,25.
Медиана: h = 3 → x₍₃₎ = 8,0. Все три совпадают с describe(). Среднее (18,43) намного больше медианы — несколько дорогих товаров тянут среднее вверх.

Выбор: столбцы, loc и iloc

ЗаписьЧто возвращает
df['price']один столбец — Series
df[['product', 'price']]несколько столбцов — DataFrame
df.loc[row_labels, col_labels]выбор по меткам; последняя метка среза включается
df.iloc[row_pos, col_pos]выбор по позициям; конец среза не включается
Python
import io
import pandas as pd

data = '''product,category,price,stock,sold
Notebook,Stationery,1.5,120,340
Pen,Stationery,0.6,300,820
Backpack,Accessories,35.0,25,48
Headphones,Electronics,49.9,15,37
USB cable,Electronics,6.5,80,150
Mug,Home,8.0,40,95
Desk lamp,Home,27.5,12,20'''
df = pd.read_csv(io.StringIO(data))
print(df['price'].head(3).tolist())
print(df.loc[2, 'product'], '|', df.iloc[0, 2])
print(df.loc[1:3, 'product':'price'])
print(df.iloc[1:3, 0:2])
df = df.set_index('product')
print(df.loc['Mug', 'price'])
▸ Ожидаемый результат
[1.5, 0.6, 35.0]
Backpack | 1.5
      product     category  price
1         Pen   Stationery    0.6
2    Backpack  Accessories   35.0
3  Headphones  Electronics   49.9
    product     category
1       Pen   Stationery
2  Backpack  Accessories
8.0
loc[1:3] вернул три строки (1, 2, 3), а iloc[1:3] — две (1, 2). После set_index('product') к строкам можно обращаться по названию товара.

Фильтрация и сортировка

Строки фильтруем булевыми масками, как в NumPy: условия объединяем через &, |, ~ и берём в скобки. isin проверяет, входит ли значение в список, а query позволяет записать условие строкой. sort_values сортирует по столбцу, а nlargest сразу возвращает n наибольших значений.

Python
import io
import pandas as pd

data = '''product,category,price,stock,sold
Notebook,Stationery,1.5,120,340
Pen,Stationery,0.6,300,820
Backpack,Accessories,35.0,25,48
Headphones,Electronics,49.9,15,37
USB cable,Electronics,6.5,80,150
Mug,Home,8.0,40,95
Desk lamp,Home,27.5,12,20'''
df = pd.read_csv(io.StringIO(data))
cheap = df[df['price'] < 10]
print(cheap[['product', 'price']])
print(df[(df['category'] == 'Electronics') & (df['stock'] < 50)]['product'].tolist())
print(df[df['category'].isin(['Home', 'Accessories'])]['product'].tolist())
print(df.query('price > 20 and sold > 30')[['product', 'sold']])
print(df.sort_values('sold', ascending=False).head(3)[['product', 'sold']])
print(df.nlargest(2, 'price')[['product', 'price']])
▸ Ожидаемый результат
     product  price
0   Notebook    1.5
1        Pen    0.6
4  USB cable    6.5
5        Mug    8.0
['Headphones']
['Backpack', 'Mug', 'Desk lamp']
      product  sold
2    Backpack    48
3  Headphones    37
     product  sold
1        Pen   820
0   Notebook   340
4  USB cable   150
      product  price
3  Headphones   49.9
2    Backpack   35.0

Новые столбцы

Новый столбец создаётся присваиванием: df['revenue'] = df['price'] * df['sold'] — без цикла, вычисление идёт по всему столбцу. Для значения по условию используют np.where, а чтобы изменить только часть строк — df.loc[маска, 'столбец'] = значение. rename и drop возвращают новый DataFrame, поэтому результат снова присваиваем переменной.

Python
import io
import numpy as np
import pandas as pd

data = '''product,category,price,stock,sold
Notebook,Stationery,1.5,120,340
Pen,Stationery,0.6,300,820
Backpack,Accessories,35.0,25,48
Headphones,Electronics,49.9,15,37
USB cable,Electronics,6.5,80,150
Mug,Home,8.0,40,95
Desk lamp,Home,27.5,12,20'''
df = pd.read_csv(io.StringIO(data))
df['revenue'] = (df['price'] * df['sold']).round(2)
df['band'] = np.where(df['price'] >= 20, 'high', 'low')
df['status'] = 'ok'
df.loc[df['stock'] < 20, 'status'] = 'reorder'
df = df.rename(columns={'sold': 'units'}).drop(columns=['category'])
print(df)
print(f"Total revenue: {df['revenue'].sum():.2f} AZN")
▸ Ожидаемый результат
      product  price  stock  units  revenue  band   status
0    Notebook    1.5    120    340    510.0   low       ok
1         Pen    0.6    300    820    492.0   low       ok
2    Backpack   35.0     25     48   1680.0  high       ok
3  Headphones   49.9     15     37   1846.3  high  reorder
4   USB cable    6.5     80    150    975.0   low       ok
5         Mug    8.0     40     95    760.0   low       ok
6   Desk lamp   27.5     12     20    550.0  high  reorder
Total revenue: 6813.30 AZN
Пример 2: доля электроники в выручке

Какой процент общей выручки по таблице выше приходится на категорию Electronics? Посчитай вручную и запиши выражение на pandas.

Показать решение
Electronics: Headphones 49,9 · 37 = 1846,3 и USB cable 6,5 · 150 = 975,0; вместе 2821,3 маната.
Общая выручка — 6813,3 маната.
Доля = 2821,3 / 6813,3 ≈ 0,414, то есть около 41,4%.
pandas: df.loc[df['category'] == 'Electronics', 'revenue'].sum() / df['revenue'].sum() (считать нужно до drop, пока столбец category ещё есть).

pandas — ежедневный инструмент аналитиков, экономистов, банков, телеком-компаний и научных лабораторий: отчёты, анализ продаж, результаты опросов, журналы датчиков. В следующем уроке займёмся пропусками, группировкой, объединением таблиц и датами.

Задание

Прочитай CSV в DataFrame. 1) Выведи списком имена студентов с баллом 80 и выше, отсортированные по баллу по убыванию; 2) выведи средний балл всех студентов, округлённый до 1 знака после запятой.

Задание · Python
import io
import pandas as pd

data = '''name,group,score
Aysel,A,91
Murad,B,76
Leyla,A,85
Elvin,B,64
Nigar,A,78
Tural,B,88'''
df = pd.read_csv(io.StringIO(data))
# 1) names with score >= 80, highest first
# 2) mean score, 1 decimal
▸ Ожидаемый результат
['Aysel', 'Tural', 'Leyla']
80.3
Задание

Добавь в таблицу заказов кафе столбец total = price * qty. Затем выведи на отдельных строках: 1) название позиции с наибольшим total; 2) список позиций, у которых total больше 25; 3) сумму всех total.

Задание · Python
import pandas as pd

orders = pd.DataFrame({
    'item': ['tea', 'coffee', 'cake', 'juice'],
    'price': [2.5, 4.0, 5.5, 3.0],
    'qty': [12, 7, 4, 9],
})
# add the total column, then print the three results
▸ Ожидаемый результат
tea
['tea', 'coffee', 'juice']
107.0

Главное

  • Series — одномерный массив с метками, DataFrame — таблица из Series с общим индексом.
  • Первый взгляд на новые данные: head(), info(), describe(), value_counts().
  • loc работает с метками и включает конец среза; iloc работает с позициями и не включает его.
  • Фильтруй масками (&, |, ~), isin и query; сортируй через sort_values и nlargest.
  • Чтобы изменить часть строк, пиши df.loc[маска, 'столбец'] = значение и избегай цепочечного присваивания.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Сколько строк вернёт df.loc[2:4] для DataFrame с индексом 0, 1, 2, …?