Углубленное изучение Pandas: структуры данных

Есть у меня определенное мнение, что понимание базовых структур данных в Pandas крайне важно, потому что это дает нам представление о фундаментальной основе тех операций, которые мы выполняем с данными. Pandas очень прост и предоставляет простой способ организации данных и манипулирования ими. Но часто бывает так, что для того, чтобы сделать что-то, выходящее за рамки мануалов из сети, мы должны сначала понять основные принципы, на которых построена технология. Структуры данных Series и DataFrame — это строительные блоки, из которых построена работа с данными в Pandas. И их понимание может помочь нам лучаше понять Pandas как продвинутый инструмент. Благодаря лучшему пониманию структур данных нам будет легче создавать эффективные пайплайны обработки данных, легче читать и поддерживать код, делать его более переиспользуемым. В конечном счете, понимая как Pandas работает на более глубоком уровне, позволит раскрыть потенциал Pandas на полную.

Это первая статья из серии статей про углубленное изучение Pandas. В ней поговорим про основные структуры данных.


Введение

Pandas — одна из наиболее важных и широко используемых библиотек анализа данных в Python. Чтобы эффективно использовать Pandas, важно понимать две основные структуры данных, которые он предлагает: Series и DataFrame.

Series и DataFrames являются важными структурами данных, которые используются для хранения, обработки и анализа данных в Pandas. В этом уроке мы дадим всестороннее представление об этих структурах данных, включая их создание и индексация внутри этих структур данных. Также поделимся особенностями работы этих структур.

Этот пост будет посвящен следующим темам:

  • Объяснение разницы между Series и DataFrame;
  • Объяснение структуры данных Series в Pandas;
  • Обсуждение разницы между списком и Series;
  • Обсуждение того, как библиотека Pandas автоматически присваивает тип данных элементам в серии;
  • Объяснение структуры и свойств DataFrame, включая его столбцы, индекс и взаимосвязь между столбцами и строками как объектами Series.

II. Разница между Series и DataFrame

Представьте, что у вас есть последовательность точек данных, представляющих одну переменную, такую как рост, вес или возраст. Эта последовательность (мы её ещё можем назвать серией точек) точек данных может быть представлена с помощью объекта Series в Pandas. Series (можно перевести как «ряд») — это одномерная структура данных, которая содержит последовательность значений и похожа на список или массив.

Вот пример Series:


0    78
1    53
2    76
3    67
4    60
Name: age, dtype: int64

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

Вот пример DataFrame:


       height     weight  age
0  187.640523  83.831507   78
1  174.001572  51.522409   53
2  179.787380  52.295150   76
3  192.408932  74.693967   67
4  188.675580  53.268766   60

Разницу между Series и DataFrame можно сравнить с разницей между линией и квадратом. Линия является одномерной и может содержать только одну последовательность точек данных, в то время как квадрат является двумерным и может содержать несколько последовательностей точек. Таким же образом, Series содержит одну последовательность точек данных, в то время как DataFrame содержит несколько последовательностей точек данных в разных столбцах.

Итак, при работе с данными в Pandas важно понимать разницу между Series и DataFrame и выбирать соответствующую структуру данных на основе размерности ваших данных.


III. Структура данных Series

Series — это одномерный индексированный массив, способный содержать любой тип данных.

Создать Series в Pandas довольно просто. Чтобы создать Series, вы просто передаете список значений в виде списка в функцию `pd.Series()` и при желании указываете индекс. Вот пример:

import pandas as pd import numpy as np s = pd.Series([1, 3, 5, np.nan, 6, 8]) print(s)

0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
5    8.0
dtype: float64

А вот пример создания Series с указанием индекса:

s = pd.Series([1, 3, 5, np.nan, 6, 8], index = [1,2,3,5,8,11]) print(s)

1     1.0
2     3.0
3     5.0
5     NaN
8     6.0
11    8.0
dtype: float64


Автоматическое определение типов

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

Например, если вы передадите список целых чисел объекту Series, Pandas автоматически присвоит серии тип данных integer. Если вы передадите список строк, Pandas присвоит серии строковый тип данных. Это делает работу с данными в Pandas удобной и легкой, так как вам не нужно беспокоиться о ручном указании типа данных для каждой серии.

Вот пример, иллюстрирующий эту мысль:


# Создаем Series из списка целых чисел
integer_series = pd.Series([1, 2, 3, 4, 5])
print("Data type of integer series:", integer_series.dtype)

# Создаем Series из списка строк
string_series = pd.Series(["apple", "banana", "cherry", "date"])
print("Data type of string series:", string_series.dtype)


Data type of integer series: int64
Data type of string series: object

Как вы можете видеть, Pandas автоматически определил тип данных элементов в списке и присвоил Series соответствующий тип данных. В первом случае список целых чисел передается объекту Series, поэтому Pandas присваивает серии целочисленный тип данных `int64`. Во втором случае список строк передается объекту Series, поэтому Pandas присваивает Series тип данных `object`, поскольку строки хранятся в Pandas как объекты.


Автоматическое определение типов при смешанных типах

Если в списке есть хотя бы один элемент с типом данных `str`, то Pandas присвоит объектный тип данных всей серии, независимо от типов данных других элементов в списке.

Вот пример, иллюстрирующий это:


# Создаем Series, содержащий как целые числа, так и строки
mixed_series = pd.Series([1, 2, "apple", 4, "banana"])
print("Data type of mixed series:", mixed_series.dtype)


Data type of mixed series: object

Как вы можете видеть, несмотря на то, что список, переданный конструктору pd.Series(), содержит как целые числа, так и строки, результирующий Series имеет тип данных `object`, поскольку в списке есть по крайней мере один элемент с типом данных `str`. Это означает, что все элементы в Series будут рассматриваться как объекты, независимо от их фактического типа данных.


Индексирование в Series

Индексирование и построение срезов (slicing) в Series работают аналогично индексации в списке или массиве. Вы можете получить доступ к отдельным элементам серии, используя квадратные скобки со значением индекса. Вот пример:


print(s[0])


1.0


print(s[:3])


0    1.0
1    3.0
2    5.0
dtype: float64


Атрибут index

Вызов атрибута `index` в Series позволяет получить объект, который содержит индексы для каждого элемента в серии. Вы можете использовать атрибут index для извлечения индексов для каждого элемента в Series и выполнения над ними различных операций.

Вот несколько примеров того, как использовать атрибут index в Series:


# Создание Series со стандартными индексами
default_index_series = pd.Series([1, 2, 3, 4, 5])

# Вызов индексов
print(default_index_series.index)


RangeIndex(start=0, stop=5, step=1)


# Создание Series с пользовательскими индексами
custom_index_series = pd.Series([1, 2, 3, 4, 5], index=["a", "b", "c", "d", "e"])

# Вызов индексов
print(custom_index_series.index)


Index(['a', 'b', 'c', 'd', 'e'], dtype='object')


# Изменение индексов
custom_index_series.index = ["x", "y", "z", "w", "v"]
print(custom_index_series)


x    1
y    2
z    3
w    4
v    5
dtype: int64

Как вы можете видеть, вы можете получить атрибут `index` из Series, просто вызвав `series.index`. Кроме того, вы можете изменить атрибут `index`, присвоив ему новое значение, как показано в примере выше. Это может быть полезно, когда вы хотите изменить метки для элементов в Series.


Разница и сходство Series и списков

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

Вот пример, иллюстрирующий это:


# Создаем серию с кастомным индексом
custom_index_series = pd.Series([1, 2, 3, 4, 5], index=["a", "b", "c", "d", "e"])
print(custom_index_series)

# Создаем серию с неинкрементальным индексом
non_incremental_index_series = pd.Series([1, 2, 3, 4, 5], index=[1, 5, 2, 6, 3])
print(non_incremental_index_series)


a    1
b    2
c    3
d    4
e    5
dtype: int64
1    1
5    2
2    3
6    4
3    5
dtype: int64

Как вы можете видеть, в первом примере показан Series с пользовательским индексом, который состоит из строк. Во втором примере показан ряд с неинкрементным индексом, который состоит из целых чисел. Это демонстрирует, что индексы в Series не обязательно должны начинаться с нуля или располагаться в порядке возрастания.


Вы также можете использовать вместе с Series различные методы Pandas, такие как `.head()`, `.tail()`, `.describe()` и т. д. Вот пример:


print(s.head())


0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
dtype: float64


print(s.describe())


count    5.000000
mean     4.600000
std      2.701851
min      1.000000
25%      3.000000
50%      5.000000
75%      6.000000
max      8.000000
dtype: float64

Существует множество операций и методов, доступных для Series, таких как арифметические операции, операции сравнения и многое другое. Вы можете использовать их для выполнения различных манипуляций с данными и вычислений.


IV. Структура данных DataFrame

DataFrame (датафрейм) — это двумерная структура данных, которая может содержать несколько типов данных в разных столбцах. Другими словами, DataFrame — это, по сути, таблица со строками и столбцами.

Создание DataFrame в Pandas выполняется с помощью конструктора `pd.DataFrame()`. Вы можете создать DataFrame из списка, словаря или массива NumPy. Вот пример использования словаря для создания DataFrame:


import pandas as pd

data = {
    'Name': ['John', 'Jane', 'Jim', 'Joan'],
    'Age': [32, 28, 41, 37],
    'City': ['New York', 'London', 'Paris', 'Berlin']
}
df = pd.DataFrame(data)
print(df)


   Name  Age      City
0  John   32  New York
1  Jane   28    London
2   Jim   41     Paris
3  Joan   37    Berlin

А вот пример создания DataFrame из списка списков:


# Создаем список списков
data = [['John', 25, 70, 180],
        ['Jane', 30, 65, 170],
        ['Jim', 35, 72, 185]]

# Создаем датафрейм из списка списков с колонками 'Name', 'Age', 'Weight', 'Height'
df = pd.DataFrame(data, columns=['Name', 'Age', 'Weight', 'Height'])

Этот код создает список списков под названием data. Каждый список внутри data представляет собой строку в DataFrame и содержит данные об имени, возрасте, весе и росте человека.

Затем мы создаем DataFrame из списка списков, передавая данные конструктору DataFrame. Мы также предоставляем список имен столбцов с аргументом columns.

Результирующий DataFrame будет выглядеть следующим образом:


   Name  Age  Weight  Height
0  John   25      70    180
1  Jane   30      65    170
2   Jim   35      72    185

Индексирование и срезы в DataFrame работают аналогично тому, как и в Series. Вы можете получить доступ к отдельным элементам, строкам или столбцам, используя квадратные скобки со значениями индекса. Вот пример:


print(df['Name'])


0    John
1    Jane
2     Jim
Name: Name, dtype: object


print(df[0:2])


   Name  Age  Weight  Height
0  John   25      70     180
1  Jane   30      65     170


Колонки — это Series

Когда вы вызываете колонки DataFrame, вы фактически получаете доступ к отдельным объектам Series, по одному для каждой колонки. Индекс внутри каждой Series совпадает с индексом DataFrame, что означает, что он содержит индексы строк внутри DataFrame.

Вот пример, иллюстрирующий это:


import pandas as pd

# Создаем DataFrame
df = pd.DataFrame({"A": [1, 2, 3, 4, 5],
                   "B": [6, 7, 8, 9, 10]})

# Получаем колонки
col_a = df["A"]
col_b = df["B"]

# Получаем индексы Series (колонок)
print(col_a.index)
print(col_b.index)

# Сравниваем индексы между собой
print(col_a.index == df.index)
print(col_b.index == df.index)


RangeIndex(start=0, stop=5, step=1)
RangeIndex(start=0, stop=5, step=1)
[ True  True  True  True  True]
[ True  True  True  True  True]

Как вы можете видеть, индекс как `col_a`, так и `col_b` совпадает с индексом DataFrame `df`, и сравнение между ними возвращает массив истинных значений, указывающий на то, что они действительно равны. Это означает, что вы можете использовать индекс DataFrame для доступа и манипулирования значениями в отдельных колонках, а также DataFrame в целом.


Атрибут name у Series

Атрибут `name` у Series позволяет получить название, присвоенное Series. Название используется для идентификации Series и может быть использовано как справочные данные, когда мы работаем с одинарной Series. В случае Series, полученного из DataFrame через доступ к его столбцам, значение атрибута `name` соответствует имени столбца, из которого был получен ряд.


np.random.seed(0)

data = {'height': np.random.normal(170, 10, 100),
        'weight': np.random.normal(65, 10, 100),
        'age': np.random.randint(18, 80, 100)}

df = pd.DataFrame(data)

height_series = df['height']

print(height_series.name)


height

Этот код выведет «height». «height» — название столбца исходного DataFrame, из которого была получена Series.


Строки — тоже Series

Когда вы извлекаете строку из DataFrame, вы тоже получаете объект Series. Индекс Series в этом случае выступает в качестве метки для каждого элемента в Series, что позволяет получать нужные данные. А ещё индекс Series эквивалентен именам колонок в DataFrame.


import pandas as pd
import numpy as np

np.random.seed(0)

data = {'height': np.random.normal(170, 10, 100),
        'weight': np.random.normal(65, 10, 100),
        'age': np.random.randint(18, 80, 100)}

df = pd.DataFrame(data)

first_row = df.loc[0]

print(first_row.index)

print(first_row['height'])


Index(['height', 'weight', 'age'], dtype='object')
187.64052345967664

Этот код выведет: Index([’height’, ’weight’, ’age’], dtype=’object’). Сначала мы получили первую строку из DataFrame (df.loc[0]) в виде Series. А затем вывели индекс этого Series. Видно, что он совпадает с именами колонок в DataFrame.

Когда вы извлекаете строку из DataFrame, вы получаете Series, и индекс серии действует как метка для каждого элемента в Series, позволяя вам ссылаться на данные при необходимости (например, таким образом выведено отдельное значение из first_row — first_row[’height’]). В этом случае индекс ряда эквивалентен именам столбцов в DataFrame.


Транспонирование

Сходство между строками и колонками в DataFrame приводит нас к операции транспонирования.

Транспонирование DataFrame — это процесс замены строк и столбцов фрейма данных так, чтобы колонки становились индексом, старый индекс становился названием колонок, а значения строк — значениями колонок. Операция транспонирования может быть сделана с помощью атрибута `T`

На уровне соединения столбцов и строк DataFrame состоит из нескольких Series, где каждый столбец представляет Series, а индекс каждой Series представляет индексы строк. Транспонирование DataFrame означает, что каждая Series-колонка становится новой строкой в транспонированном DataFrame, а метки строк каждой Series становятся новым индексом для транспонированного DataFrame.

Например, рассмотрим следующий код:


np.random.seed(0)

data = {'height': np.random.normal(170, 10, 100),
        'weight': np.random.normal(65, 10, 100),
        'age': np.random.randint(18, 80, 100)}

df = pd.DataFrame(data)

transposed_df = df.T

print(transposed_df)


                0           1          2           3           4           5   \
height  187.640523  174.001572  179.78738  192.408932  188.675580  160.227221   
weight   83.831507   51.522409   52.29515   74.693967   53.268766   84.436212   
age      78.000000   53.000000   76.00000   67.000000   60.000000   27.000000   

                6           7           8           9   ...          90  \
height  179.500884  168.486428  168.967811  174.105985  ...  165.968231   
weight   60.863810   57.525452   84.229420   79.805148  ...   52.071431   
age      62.000000   31.000000   75.000000   24.000000  ...   27.000000   

                91          92          93          94          95  \
height  182.224451  172.082750  179.766390  173.563664  177.065732   
weight   67.670509   64.607172   53.319065   70.232767   63.284537   
age      78.000000   45.000000   65.000000   53.000000   37.000000   

                96          97          98          99  
height  170.105000  187.858705  171.269121  174.019894  
weight   72.717906   73.235042   86.632359   78.365279  
age      30.000000   36.000000   67.000000   28.000000  

[3 rows x 100 columns]

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


V. Заключение

Библиотека Pandas для Python предоставляет эффективные структуры данных для хранения данных и манипулирования ими. Структуры Series и DataFrame являются основными объектами в Pandas. Series — это одномерный индексированный массив, а DataFrame — это двумерная структура данных с индексированными строками и столбцами.

DataFrame создаются из объектов Series, и каждая колонка в DataFrame представляет собой Series со своей собственной меткой. Таким же образом, когда мы обращаемся к строке в DataFrame, мы получаем объект Series, и индекс внутри этого объекта совпадает с индексом всего DataFrame.

Важно глубоко понимать, как работают структуры данных Series и DataFrame в Pandas, поскольку они формируют основу всех операций анализа данных в Pandas. Понимание основных свойств, таких как индекс и тип данных, имеет важное значение для эффективного использования Pandas для задач анализа данных. Понимание взаимосвязи между Series и DataFrame помогает лучше обрабатывать данные и манипулировать ими. Знание основополагающих принципов и операций с базовыми структурами данными в Pandas приводит к более эффективному использованию библиотеки при решении реальных проблем с данными.

Поделиться
Отправить
Запинить
2 комментария
Линар Давлетгареев

Хорошая статья. Спасибо!)

Елена Я.

Спасибо!

Популярное