Урок курса

Series и DataFrame: создание, index, columns, shape, dtypes

NumPy и pandas: практический тренажёр

В NumPy мы работали с ndarray — однородным массивом, у которого есть shape и dtype, но нет именованных осей. pandas строится поверх этой идеи и добавляет ключевое: каждая строка и каждый столбец получают метку. Начнём с самого простого объекта — Series.

Создание Series: из списка и из словаря

Series — это одномерный объект pandas: последовательность значений, у каждого из которых есть метка. Метки хранятся в атрибуте index, значения — в values.

Из списка. Когда передаёшь список, pandas сам генерирует целочисленный индекс начиная с 0:

import pandas as pd

s = pd.Series([10, 20, 30])
print(s)
# 0    10
# 1    20
# 2    30
# dtype: int64

print(s.index)   # RangeIndex(start=0, stop=3, step=1)
print(s.values)  # array([10, 20, 30])

RangeIndex — это не просто список чисел, а ленивый объект, аналогичный range. Pandas не хранит все метки явно в памяти, пока они стандартные целые числа от 0 до n.

Из словаря. Когда передаёшь словарь, ключи становятся метками index, значения — данными:

s2 = pd.Series({'x': 10, 'y': 20, 'z': 30})
print(s2)
# x    10
# y    20
# z    30
# dtype: int64

print(list(s2.index))   # ['x', 'y', 'z']
print(list(s2.values))  # [10, 20, 30]

Порядок меток соответствует порядку ключей в словаре — в Python 3.7+ словари упорядочены, поэтому результат предсказуем.

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

Создание DataFrame: из словаря списков и списка словарей

DataFrame — это двумерная структура: таблица со строками и столбцами. В отличие от Series, у неё есть два набора меток: index для строк и columns для столбцов.

Создать DataFrame можно двумя основными способами, и оба дают одинаковый результат при разных входных форматах.

Способ 1: словарь списков. Ключи словаря становятся именами столбцов, а каждый список — данными этого столбца:

import pandas as pd

df = pd.DataFrame({
    'name':  ['Анна', 'Борис', 'Вера'],
    'score': [85, 92, 78],
    'passed': [True, True, False]
})
print(df)
#     name  score  passed
# 0   Анна     85    True
# 1  Борис     92    True
# 2   Вера     78   False

Здесь мышление «колонко-центричное»: ты сначала думаешь о столбцах, потом pandas собирает из них строки. Все списки обязаны быть одной длины — иначе pandas выбросит ошибку.

Способ 2: список словарей. Каждый словарь — одна строка, ключи — имена столбцов:

df2 = pd.DataFrame([
    {'name': 'Анна',  'score': 85, 'passed': True},
    {'name': 'Борис', 'score': 92, 'passed': True},
    {'name': 'Вера',  'score': 78, 'passed': False}
])
print(df2)
#     name  score  passed
# 0   Анна     85    True
# 1  Борис     92    True
# 2   Вера     78   False

Здесь мышление «строко-центричное»: каждая запись — один объект. Этот формат часто встречается, когда данные приходят из API или JSON-файлов — список объектов, где каждый объект описывает одну сущность.

Оба вызова дают структурно одинаковый DataFrame: те же столбцы, те же строки, тот же автоматический целочисленный index 0, 1, 2, …

Какой способ выбрать — зависит от источника данных. Если ты формируешь таблицу из готовых колонок (например, результатов вычислений), удобнее словарь списков. Если накапливаешь строки в цикле — удобнее список словарей, и в конце один вызов pd.DataFrame(rows) собирает всё вместе.

Атрибуты index, columns, shape и dtypes

Когда объект создан, нужно уметь быстро понять его структуру: сколько строк и столбцов, какие у них метки, какие типы данных. Для этого у Series и DataFrame есть несколько атрибутов, которые читаются без вызова — просто обращение к имени.

shape работает одинаково для обоих объектов и возвращает кортеж размерностей. У Series это (n,) — один элемент, потому что структура одномерная. У DataFrame — (n_rows, n_cols). Это прямой аналог ndarray.shape, синтаксис идентичен.

index и columns — наборы меток. У Series есть только index. У DataFrame есть оба: index для строк, columns для столбцов. Оба атрибута возвращают объект типа Index, который можно обернуть в list(), чтобы получить обычный список меток.

dtype и dtypes — типы данных. У Series — s.dtype (единственное число), потому что у неё один массив значений. У DataFrame — df.dtypes (множественное число), потому что каждый столбец может хранить свой тип. df.dtypes возвращает отдельный Series, где индекс — имена столбцов, а значения — их типы.

В примерах этого урока встречаются три типа: int64 для целых чисел, float64 для дробных, object для строк. object — это то, что pandas назначает столбцу со строковыми или смешанными значениями при стандартном создании DataFrame из Python-списков.

Вот как это выглядит на практике:

import pandas as pd

# Series из списка
s1 = pd.Series([10, 20, 30])
print(s1.shape)   # (3,)
print(s1.dtype)   # int64
print(s1.index)   # RangeIndex(start=0, stop=3, step=1)

# Series из словаря
s2 = pd.Series({'a': 1.5, 'b': 2.5, 'c': 3.5})
print(s2.shape)        # (3,)
print(s2.dtype)        # float64
print(list(s2.index))  # ['a', 'b', 'c']

# DataFrame
df = pd.DataFrame({
    'name':   ['Анна', 'Борис', 'Вера'],
    'score':  [85, 92, 78],
    'passed': [True, True, False]
})
print(df.shape)          # (3, 3)
print(list(df.columns))  # ['name', 'score', 'passed']
print(list(df.index))    # [0, 1, 2]
print(df.dtypes)
# name      object
# score      int64
# passed      bool
# dtype: object

На что смотреть в выводе df.dtypes: столбец name получил тип object, потому что содержит строки — именно так pandas представляет строковые данные при создании из обычных Python-списков. Столбец score — int64, passed — bool.

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

s3 = pd.Series([1, 2, None])
print(s3.dtype)   # float64
print(s3)         # 0    1.0  1    2.0  2    NaN  dtype: float64

Практика чтения этих четырёх атрибутов стандартная: после создания любой таблицы сначала смотришь на shape, чтобы понять размер, потом на dtypes, чтобы убедиться, что типы подхватились правильно. Если score вдруг оказался object — значит, где-то в данных затесалась строка.

Метка (label) и позиция (position): в чём разница

У каждой строки в Series или DataFrame есть два способа её идентифицировать: по метке и по позиции. Это разные вещи, хотя при стандартном целочисленном индексе они выглядят одинаково.

Позиция — это порядковый номер элемента, считая от нуля. Первый элемент всегда на позиции 0, второй — на позиции 1, и так далее. Позиция не хранится явно нигде: она просто следует из порядка.

Метка — это значение из index, которое pandas хранит и использует для обращения к строке по имени. Метка может быть целым числом, строкой, датой — чем угодно.

Когда создаёшь Series из списка, pandas автоматически ставит метки 0, 1, 2, … Это RangeIndex, и в нём метка и позиция совпадают численно:

import pandas as pd

s = pd.Series([100, 200, 300])
print(list(s.index))  # [0, 1, 2]

Элемент на позиции 0 имеет метку 0, на позиции 1 — метку 1. Выглядит одинаково, но это совпадение, а не тождество.

Как только создаёшь Series из словаря с нецелочисленными ключами, совпадение пропадает:

s2 = pd.Series({'x': 10, 'y': 20, 'z': 30})
print(list(s2.index))  # ['x', 'y', 'z']

Здесь метки — это 'x', 'y', 'z'. Позиции по-прежнему 0, 1, 2. Метка 'y' находится на позиции 1 — это разные идентификаторы одного и того же элемента.

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

Особенно наглядно разрыв между меткой и позицией виден, когда DataFrame создаётся с нестандартным индексом. Представь, что данные уже пришли в таком порядке — строки с метками 2, 0, 1:

df = pd.DataFrame(
    {
        'name':  ['Вера', 'Анна', 'Борис'],
        'score': [78, 85, 92]
    },
    index=[2, 0, 1]  # метки строк заданы явно
)
print(df)
#     name  score
# 2   Вера     78
# 0   Анна     85
# 1  Борис     92

print(df.index.tolist())  # [2, 0, 1]

Строка с меткой 2 (Вера) находится на позиции 0 — первая физически. Строка с меткой 0 (Анна) — на позиции 1. Метки здесь не совпадают с позициями: метка 0 не означает «первая строка».

Если обращаться к этому DataFrame по позиции, на нулевой позиции окажется Вера. Если по метке 0 — Анна. Это ровно та точка, где путаница между меткой и позицией начинает давать неверные результаты — и именно поэтому pandas предоставляет разные инструменты для каждого из этих двух способов обращения к данным.

Задача 1

Series создан командой s = pd.Series({'p': 1, 'q': 2, 'r': 3}). Что вернёт list(s.index)?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 2

DataFrame создан так: df = pd.DataFrame([{'x': 1, 'y': 2}, {'x': 3, 'y': 4}, {'x': 5, 'y': 6}]). Чему равен df.shape?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 3

Series и DataFrame: создание из двух форматов и чтение shape, dtypes, index, columns

Что уже дано

Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.

Что нужно сделать

Напишите программу, которая:

  1. Читает из stdin целое число n — количество строк таблицы.
  2. Читает следующие n строк; каждая содержит три токена через пробел: название (строка без пробелов), цена (целое число), количество (целое число). Пример строки: Стол 5000 12.
  3. Создаёт pd.Series с именем prices из списка цен (в том порядке, в котором они прочитаны). Индекс — автоматический RangeIndex.
  4. Создаёт pd.DataFrame с именем df из словаря списков с тремя ключами: 'name', 'price', 'qty' — в таком порядке.
  5. Выводит в stdout ровно шесть строк:
  • Строка 1: prices.shape в виде (n,).
  • Строка 2: prices.dtype — строковое представление типа.
  • Строка 3: df.shape в виде (n_rows, n_cols).
  • Строка 4: list(df.columns) — список имён столбцов через стандартный print.
  • Строка 5: list(df.index) — список меток строк.
  • Строка 6: три пары имя_столбца:dtype через пробел, по одной на каждый столбец df в порядке df.dtypes.items().

Для строки 6 используйте формат name:object price:int64 qty:int64.

Ввод и вывод

Программа читает stdin и пишет в stdout.

Формат ввода

n
название1 цена1 количество1
название2 цена2 количество2
...

Дополнительные ограничения: n >= 1.

Формат вывода

(n,)
dtype_series
(n, 3)
['name', 'price', 'qty']
[0, 1, ..., n-1]
name:object price:int64 qty:int64
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задача 4

Series из словаря: метки, позиции и атрибуты index, shape, dtype

Что уже дано

Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.

Что нужно сделать

Напишите программу, которая:

  1. Читает из stdin целое число k — количество пар.
  2. Читает следующие k строк; каждая содержит два токена через пробел: метка (строка без пробелов) и значение (целое число). Пример строки: alpha 42.
  3. Создаёт pd.Series из словаря {метка: значение, …} в порядке прочитанных строк — назовите его s.
  4. Выводит в stdout ровно k + 4 строк в следующем порядке:
  • Строки 1…k: для каждого i от 0 до k-1 — одну строку вида label=<метка> pos=<i> value=<значение>, где <метка> — s.index[i], <i> — позиция (целое число), <значение> — s.iloc[i].
  • Строка k+1: shape=<shape> — кортеж вида (k,).
  • Строка k+2: dtype=<dtype> — строковое представление s.dtype.
  • Строка k+3: index_type=<тип> — имя класса объекта s.index, получите через type(s.index).__name__.
  • Строка k+4: first_label_is_pos_0=<bool> — результат сравнения s.index[0] == 0 в виде True или False.

Последняя строка демонстрирует ключевую идею: когда индекс — строки, метка первого элемента не равна его позиции 0.

Ввод и вывод

Программа читает stdin и пишет в stdout.

Формат ввода

k
метка1 значение1
метка2 значение2
...

Дополнительные ограничения: k >= 1; все строковые метки попарно различны.

Формат вывода

label=метка1 pos=0 value=значение1
label=метка2 pos=1 value=значение2
...
shape=(k,)
dtype=int64
index_type=Index
first_label_is_pos_0=False
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задача 5

DataFrame из списка словарей: dtypes, метки и позиции строк

Что уже дано

Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.

Что нужно сделать

Напишите программу, которая:

  1. Читает из stdin целое число n — количество строк таблицы.
  2. Читает следующие n строк; каждая содержит три токена через пробел: город (строка без пробелов), население (целое число), площадь (вещественное число, может быть целым). Пример строки: Казань 1200000 614.0.
  3. Формирует список словарей, где каждый словарь имеет ключи 'city', 'population', 'area' — в таком порядке. Создаёт pd.DataFrame из этого списка словарей; называет его df.
  4. Выводит в stdout ровно n + 5 строк в следующем порядке:
  • Строки 1…n: для каждой позиции i от 0 до n-1 — строку вида pos=<i> label=<метка> city=<значение df['city'].iloc[i]>, где <метка> — df.index[i] (целое число).
  • Строка n+1: shape=<shape> — кортеж вида (n, 3).
  • Строка n+2: columns=<список> — list(df.columns) через стандартный print.
  • Строка n+3: city_dtype=<dtype> — df.dtypes['city'].
  • Строка n+4: population_dtype=<dtype> — df.dtypes['population'].
  • Строка n+5: area_dtype=<dtype> — df.dtypes['area'].

Для строк n+3…n+5 используйте формат city_dtype=object, population_dtype=int64, area_dtype=float64.

Ввод и вывод

Программа читает stdin и пишет в stdout.

Формат ввода

n
город1 население1 площадь1
город2 население2 площадь2
...

Дополнительные ограничения: n >= 1.

Формат вывода

pos=0 label=0 city=город1
pos=1 label=1 city=город2
...
shape=(n, 3)
columns=['city', 'population', 'area']
city_dtype=object
population_dtype=int64
area_dtype=float64
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задача 6

Найди и исправь: три ошибки в создании Series/DataFrame и чтении атрибутов

Что уже дано

Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет.

В редакторе уже есть сломанный код. В нём ровно три ошибки в трёх независимых строках:

  1. pd.Series создаётся из словаря {'a': 1, 'b': 2, 'c': 3}, но при выводе index_type используется type(s.values).__name__ вместо type(s.index).__name__ — в результате выводится тип массива значений, а не тип объекта-индекса.
  2. pd.DataFrame создаётся из словаря списков, но в коде написано pd.DataFrame([cols]), где cols — сам словарь, обёрнутый в список. Это создаёт DataFrame из одного словаря-строки вместо словаря столбцов. Нужно передать словарь напрямую: pd.DataFrame(cols).
  3. При выводе shape DataFrame используется df.shape[0] вместо df.shape — печатается только число строк, а не полный кортеж.

Каждая ошибка находится в отдельной строке; исправление каждой не затрагивает остальные.

Что нужно сделать

Найдите и исправьте все три ошибки так, чтобы программа:

  1. Читала из stdin целое число n — количество записей.
  2. Читала следующие n строк; каждая содержит два токена через пробел: метка (строка без пробелов) и значение (целое число).
  3. Создавала pd.Series из словаря {метка: значение, …} в порядке прочитанных строк — имя переменной s.
  4. Создавала pd.DataFrame из словаря списков с двумя столбцами: 'label' (список меток) и 'value' (список значений) — имя переменной df.
  5. Выводила в stdout ровно пять строк:
  • Строка 1: index_type=<тип> — имя класса s.index через type(s.index).__name__.
  • Строка 2: s_shape=<shape> — кортеж вида (n,).
  • Строка 3: s_dtype=<dtype> — строковое представление s.dtype.
  • Строка 4: df_shape=<shape> — полный кортеж вида (n, 2).
  • Строка 5: first_label_is_pos_0=<bool> — результат s.index[0] == 0 в виде True или False.

Ввод и вывод

Программа читает stdin и пишет в stdout.

Формат ввода

n
метка1 значение1
метка2 значение2
...

Дополнительные ограничения: n >= 1; все строковые метки попарно различны.

Формат вывода

index_type=Index
s_shape=(n,)
s_dtype=int64
df_shape=(n, 2)
first_label_is_pos_0=False
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задачи к уроку

Все задачи курса →

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку