В отличие от других структур данных в Python pandas не только пользуется преимуществами высокой производительности массивов NumPy, но и добавляет в них индексы.
Этот выбор оказался крайне удачным. Несмотря на и без того отличную гибкость, которая обеспечивается существующими динамическими структурами, внутренние ссылки на их элементы (а именно ими и являются метки) позволяют разработчикам еще сильнее упрощать операции.
В этом разделе речь пойдет о некоторых базовых функциях, использующих этот механизм:
- Переиндексирование
- Удаление
- Выравнивание
Переиндексирование df.reindex()
Вы уже знаете, что после объявления в структуре данных объект Index
нельзя менять. Но с помощью операции переиндексирования это можно решить.
Существует даже возможность получить новую структуру из уже существующей, где правила индексирования заданы заново.
>>> ser = pd.Series([2,5,7,4], index=['one','two','three','four']) >>> ser
one 2
two 5
three 7
four 4
dtype: int64
Для того чтобы провести переиндексирование объекта Series
библиотека pandas предоставляет функцию reindex()
. Она создает новый объект Series
со значениями из другого Series
, которые теперь переставлены в соответствии с новой последовательностью меток.
При операции переиндексирования можно поменять порядок индексов, удалить некоторые из них или добавить новые. Если метка новая, pandas добавит NaN
на место соответствующего значения.
>>> ser.reindex(['three','four','five','one'])
three 7.0
four 4.0
five NaN
one 2.0
dtype: float64
Как видно по выводу, порядок меток можно поменять полностью. Значение, которое раньше соответствовало метке two
, удалено, зато есть новое с меткой five
.
Тем не менее в случае, например, с большим Dataframe
, не совсем удобно будет указывать новый список меток. Вместо этого можно использовать метод, который заполняет или интерполирует значения автоматически.
Для лучшего понимания механизма работы этого режима автоматического индексирования создадим следующий объект Series
.
>>> ser3 = pd.Series([1,5,6,3],index=[0,3,5,6])
>>> ser3
0 1
3 5
5 6
6 3
dtype: int64
В этом примере видно, что колонка с индексами — это не идеальная последовательность чисел. Здесь пропущены цифры 1, 2 и 4. В таком случае нужно выполнить операцию интерполяции и получить полную последовательность чисел. Для этого можно использовать reindex
с параметром method
равным ffill
. Более того, необходимо задать диапазон значений для индексов. Тут можно использовать range(6)
в качестве аргумента.
>>> ser3.reindex(range(6),method='ffill')
0 1
1 1
2 1
3 5
4 5
5 6
dtype: int64
Теперь в объекте есть элементы, которых не было в оригинальном объекте Series
. Операция интерполяции сделала так, что наименьшие индексы стали значениями в объекте. Так, индексы 1 и 2 имеют значение 1, принадлежащее индексу 0.
Если нужно присваивать значения индексов при интерполяции, необходимо использовать метод bfill
.
>>> ser3.reindex(range(6),method='bfill')
0 1
1 5
2 5
3 5
4 6
5 6
dtype: int64
В этом случае значения индексов 1 и 2 равны 5, которое принадлежит индексу 3.
Операция отлично работает не только с Series
, но и с Dataframe
. Переиндексирование можно проводить не только на индексах (строках), но также и на колонках или на обоих. Как уже отмечалось, добавлять новые индексы и колонки возможно, но поскольку в оригинальной структуре есть недостающие значения, на их месте будет NaN
.
>>> frame.reindex(range(5), method='ffill',columns=['colors','price','new', 'object'])
item | colors | price | new | object |
---|---|---|---|---|
id | ||||
0 | blue | 1.2 | blue | ball |
1 | green | 1.0 | green | pen |
2 | yellow | 3.3 | yellow | pencil |
3 | red | 0.9 | red | paper |
4 | white | 1.7 | white | mug |
Удаление
Еще одна операция, связанная с объектами Index
— удаление. Удалить строку или колонку не составит труда, потому что метки используются для обозначения индексов и названий колонок.
В этом случае pandas предоставляет специальную функцию для этой операции, которая называется drop()
. Метод возвращает новый объект без элементов, которые необходимо было удалить.
Например, возьмем в качестве примера случай, где из объекта нужно удалить один элемент. Для этого определим базовый объект Series
из четырех элементов с 4 отдельными метками.
>>> ser = pd.Series(np.arange(4.), index=['red','blue','yellow','white'])
>>> ser
red 0.0
blue 1.0
yellow 2.0
white 3.0
dtype: float64
Теперь, предположим, необходимо удалить объект с меткой yellow
. Для этого нужно всего лишь указать ее в качестве аргумента функции drop()
.
>>> ser.drop('yellow')
red 0.0
blue 1.0
white 3.0
dtype: float64
Для удаления большего количества элементов, передайте массив с соответствующими индексами.
>>> ser.drop(['blue','white'])
red 0.0
yellow 2.0
dtype: float64
Если речь идет об объекте Dataframe
, значения могут быть удалены с помощью ссылок на метки обеих осей. Возьмем в качестве примера следующий объект.
>>> frame = pd.DataFrame(np.arange(16).reshape((4,4)),
... index=['red', 'blue', 'yellow', 'white'],
... columns=['ball', 'pen', 'pencil', 'paper'])
>>> frame
ball | pen | pencil | paper | |
---|---|---|---|---|
red | 0 | 1 | 2 | 3 |
blue | 4 | 5 | 6 | 7 |
yellow | 8 | 9 | 10 | 11 |
white | 12 | 13 | 14 | 15 |
Для удаления строк просто передайте индексы строк.
>>> frame.drop(['blue','yellow'])
ball | pen | pencil | paper | |
---|---|---|---|---|
red | 0 | 1 | 2 | 3 |
white | 12 | 13 | 14 | 15 |
Для удаления колонок необходимо указывать индексы колонок, а также ось, с которой требуется удалить элементы. Для этого используется параметр axis
. Чтобы сослаться на название колонки, нужно написать axis=1
.
>>> frame.drop(['pen','pencil'],axis=1)
ball | paper | |
---|---|---|
red | 0 | 3 |
blue | 4 | 7 |
yellow | 8 | 11 |
white | 12 | 15 |
Арифметика и выравнивание данных
Наверное, самая важная особенность индексов в этой структуре данных — тот факт, что pandas может выравнивать индексы двух разных структур. Это особенно важно при выполнении арифметических операций на их значениях. В этом случае индексы могут быть не только в разном порядке, но и присутствовать лишь в одной из двух структур.
В качестве примера можно взять два объекта Series
с разными метками.
>>> s1 = pd.Series([3,2,5,1],['white','yellow','green','blue'])
>>> s2 = pd.Series([1,4,7,2,1],['white','yellow','black','blue','brown'])
Теперь воспользуемся базовой операцией сложения. Как видно по примеру, некоторые метки есть в обоих структурах, а остальные — только в одной. Если они есть в обоих случаях, их значения складываются, а если только в одном — то значением будет NaN
.
>>> s1 + s2
black NaN
blue 3.0
brown NaN
green NaN
white 4.0
yellow 6.0
dtype: float64
При использовании Dataframe
выравнивание работает по тому же принципу, но проводится и для рядов, и для колонок.
>>> frame1 = pd.DataFrame(np.arange(16).reshape((4,4)),
... index=['red','blue','yellow','white'],
... columns=['ball','pen','pencil','paper'])
>>> frame2 = pd.DataFrame(np.arange(12).reshape((4,3)),
... index=['blue','green','white','yellow'],
... columns=['mug','pen','ball'])
>>> frame1
ball | pen | pencil | paper | |
---|---|---|---|---|
red | 0 | 1 | 2 | 3 |
blue | 4 | 5 | 6 | 7 |
yellow | 8 | 9 | 10 | 11 |
white | 12 | 13 | 14 | 15 |
>>> frame2
mug | pen | ball | |
---|---|---|---|
blue | 0 | 1 | 2 |
green | 3 | 4 | 5 |
white | 6 | 7 | 8 |
yellow | 9 | 10 | 11 |
>>> frame1 + frame2
ball | mug | paper | pen | pencil | |
---|---|---|---|---|---|
blue | 6.0 | NaN | NaN | 6.0 | NaN |
green | NaN | NaN | NaN | NaN | NaN |
red | NaN | NaN | NaN | NaN | NaN |
white | 20.0 | NaN | NaN | 20.0 | NaN |
yellow | 19.0 | NaN | NaN | 19.0 | NaN |