Приведем основные подходы к работе с данными, содержащимися в Series
и DataFrame
.
Важный метод в объектах pandas
— это reindex
, который создает
новый объект с данными, согласованными с новым индексом. Рассмотрим
пример:
In [84]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
In [85]: obj
Out[85]:
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
Вызов reindex
в объекте Series
переупорядочивает данные в
соответствии с новым индексом, вводя пропущенные значения, если
какие-либо значения индекса еще не присутствовали:
In [86]: obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
In [87]: obj2
Out[87]:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
ля упорядоченных данных, таких как временные ряды, может быть
желательно выполнить некоторую интерполяцию или заполнение значений
при переиндексации. Аргумент method
позволяет нам сделать это,
используя метод такой как ffill
(forward-fill), который заполняет
«вперед» значениями ряд:
In [88]: obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
In [89]: obj3
Out[89]:
0 blue
2 purple
4 yellow
dtype: object
In [90]: obj3.reindex(range(6), method='ffill')
Out[90]:
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: object
В объектах DataFrame
метод reindex
может изменять либо индекс
(строки), столбцы, либо и то и то. Когда передается только одна
последовательность, то переиндексируются строки:
In [91]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
...: index=['a', 'c', 'd'],
...: columns=['Ohio', 'Texas', 'California'])
In [92]: frame
Out[92]:
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
In [93]: frame2 = frame.reindex(['a', 'b', 'c', 'd'])
In [94]: frame2
Out[94]:
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
Столбцы переиндексируются с помощью аргумента columns
:
In [95]: states = ['Texas', 'Utah', 'California']
In [96]: frame.reindex(columns=states)
Out[96]:
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
В таблице pandas:functionality:tbl:1 представлены аргументы
функции reindex
.
Таблица 3. Аргументы функции reindex
Аргумент | Описание |
index | Новая последовательность для использования в качестве индекса. Может быть экземпляром Index или любой последовательности Python |
method | Метод интерполяции (заполнения): ffil (forward-fill) — прямое заполнение, bfill (backward-fill) — обратное заполнение |
fill_value | Подставляется это значения при заполнении пропущенных данных, которые появляются при переиндексации |
limit | При заполнении задает максимальный размер шага (по количеству элементов) заполнения |
tolerance | При заполнении задает максимальный размер шага (в абсолютном числовом расстоянии) для заполнения неточных совпадений |
level | Сопоставляет простой Index на уровне MultiIndex ; в противном случае выбирает подмножество |
copy | Если True , всегда копирует данные, даже если новый индекс эквивалентен старому; если False не копирует данные, если индексы эквивалентны |
Удалить одну или несколько записей легко, если имеется массив или
список индексов, которые не содержат эти записи. Поскольку это может
потребовать некоторых операций над множествами, метод drop
возвращает новый объект с указанными значениями, удаленными с оси:
In [97]: obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
In [98]: obj
Out[98]:
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
In [99]: new_obj = obj.drop('c')
In [100]: new_obj
Out[100]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
In [101]: obj.drop(['d', 'c'])
Out[101]:
a 0.0
b 1.0
e 4.0
dtype: float64
В DataFrame
значения индекса могут быть удалены с любой оси:
In [102]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
...: index=['Ohio', 'Colorado', 'Utah', 'New York'],
...: columns=['one', 'two', 'three', 'four'])
In [103]: data
Out[103]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
Вызов drop
с последовательностью меток удаляет значения из меток
строк (ось 0
):
In [104]: data.drop(['Colorado', 'Ohio'])
Out[104]:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
Удалить значения в столбцах можно передавая параметр axis=1
или
axis=columns
:
In [105]: data.drop('two', axis=1)
Out[105]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
In [106]: data.drop(['two', 'four'], axis='columns')
Out[106]:
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
New York 12 14
Многие функции, такие как drop
, которые изменяют размер или форму
Series
или DataFrame
, могут изменять сам объект (in-place) без
создания нового объекта:
In [107]: obj.drop('c', inplace=True)
In [108]: obj
Out[108]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
Будьте осторожны с параметром inplace
, так как происходит удаление
данных.
Важной особенностью pandas
для некоторых приложений является поведение
арифметических операций между объектами с разными индексами. При
сложении объекты в случае, когда любые пары индексов отличаются,
соответствующий индекс в результате является объединением исходных
индексов:
In [109]: s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
In [110]: s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
In [111]: s1
Out[111]:
a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64
In [112]: s2
Out[112]:
a -2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64
In [113]: s1 + s2
Out[113]:
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
Выравнивание данных вводит пропущенные значения в местах меток, которые не пересекаются. Пропущенные значения будут распространяться в дальнейших арифметических вычислениях.
В случае DataFrame
выравнивание осуществляется как для строк, так и
для столбцов:
In [114]: df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
...: index=['Ohio', 'Texas', 'Colorado'])
In [115]: df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
...: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [116]: df1
Out[116]:
b c d
Ohio 0.0 1.0 2.0
Texas 3.0 4.0 5.0
Colorado 6.0 7.0 8.0
In [117]: df2
Out[117]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
Сумма введенных объектов вернет новый объект DataFrame
, чьи индексы
и столбцы являются объединениями индексов и столбцов двух складываемых
объектов:
In [118]: df1 + df2
Out[118]:
b c d e
Colorado NaN NaN NaN NaN
Ohio 3.0 NaN 6.0 NaN
Oregon NaN NaN NaN NaN
Texas 9.0 NaN 12.0 NaN
Utah NaN NaN NaN NaN
Так как столбцы 'c'
и 'e'
не находятся одновременно в обоих
объектах DataFrame
, в результате они содержат отсутствующие
значения. Такое же происходит и со строками.
Если сложить объекты DataFrame
без общих меток столбцов или строк,
результат будет содержать все отсутствующие значения:
In [119]: df1 = pd.DataFrame({'A': [1, 2]})
In [120]: df2 = pd.DataFrame({'B': [3, 4]})
In [121]: df1
Out[121]:
A
0 1
1 2
In [122]: df2
Out[122]:
B
0 3
1 4
In [123]: df1 - df2
Out[123]:
A B
0 NaN NaN
1 NaN NaN
In [124]: df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
...: columns=list('abcd'))
In [125]: df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
...: columns=list('abcde'))
In [126]: df2.loc[1, 'b'] = np.nan
In [127]: df1
Out[127]:
a b c d
0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.0 11.0
In [128]: df2
Out[128]:
a b c d e
0 0.0 1.0 2.0 3.0 4.0
1 5.0 NaN 7.0 8.0 9.0
2 10.0 11.0 12.0 13.0 14.0
3 15.0 16.0 17.0 18.0 19.0
Сложение этих объектов приводит к значениям NA в местах, которые не перекрываются:
In [129]: df1 + df2
Out[129]:
a b c d e
0 0.0 2.0 4.0 6.0 NaN
1 9.0 NaN 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
Для заполнения отсутствующих значений можно воспользоваться функцией
add
с дополнительным аргументом fill_value
:
In [130]: df1.add(df2, fill_value=0)
Out[130]:
a b c d e
0 0.0 2.0 4.0 6.0 4.0
1 9.0 5.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
В таблице pandas:functionality:tbl:2 представлены методы для
арифметических операций. У каждого из них есть аналог, начинающийся с
буквы r
, у которого переставлены аргументы. Приведенные ниже
примеры эквивалентны:
In [131]: 1 / df1
Out[131]:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909
In [132]: df1.rdiv(1)
Out[132]:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909
Соответственно, при переиндексации Series
или DataFrame
вы также
можете указать другое значение заполнения:
In [133]: df1.reindex(columns=df2.columns, fill_value=0)
Out[133]:
a b c d e
0 0.0 1.0 2.0 3.0 0
1 4.0 5.0 6.0 7.0 0
2 8.0 9.0 10.0 11.0 0
Таблица 4. Гибкие арифметические методы
Метод | Описание |
add , radd | Сложение (+ ) |
sub , rsub | Вычитание (- ) |
div , rdiv | Деление (/ ) |
floordiv , rfloordiv | Целочисленное деление (// ) |
mul , rmul | Умножение (* ) |
pow , rpow | Возведение в степень (** ) |
DataFrame
и Series
Как и для массивов NumPy разной размерности, существуют арифметические
операции между объектами DataFrame
и Series
. В качестве примера
рассмотрим разность между двумерным массивом и одной из его строк:
In [134]: arr = np.arange(12.).reshape((3, 4))
In [135]: arr
Out[135]:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
In [136]: arr[0]
Out[136]: array([0., 1., 2., 3.])
In [137]: arr - arr[0]
Out[137]:
array([[0., 0., 0., 0.],
[4., 4., 4., 4.],
[8., 8., 8., 8.]])
При вычитании arr[0]
из arr
, операция осуществляется для каждой
строки. Операции между DataFrame
и Series
производятся аналогично.
In [138]: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
...: columns=list('bde'),
...: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [139]: series = frame.iloc[0]
In [140]: frame
Out[140]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
In [141]: series
Out[141]:
b 0.0
d 1.0
e 2.0
Name: Utah, dtype: float64
По умолчанию арифметические операции между DataFrame
и Series
приводят индексы объекта Series
к столбцам объекта DataFrame
,
распространяя операцию по строкам:
In [142]: frame - series
Out[142]:
b d e
Utah 0.0 0.0 0.0
Ohio 3.0 3.0 3.0
Texas 6.0 6.0 6.0
Oregon 9.0 9.0 9.0
Если значение индекса не найдено ни в столбцах DataFrame
, ни в индексе
Series
, объекты будут переиндексированы для формирования
объединения:
In [143]: series2 = pd.Series(range(3), index=['b', 'e', 'f'])
In [144]: frame + series2
Out[144]:
b d e f
Utah 0.0 NaN 3.0 NaN
Ohio 3.0 NaN 6.0 NaN
Texas 6.0 NaN 9.0 NaN
Oregon 9.0 NaN 12.0 NaN
Если вместо согласования по столбцам нужно согласовывать операцию по строкам, нужно использовать арифметический метод:
In [145]: series3 = frame['d']
In [146]: frame
Out[146]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
In [147]: series3
Out[147]:
Utah 1.0
Ohio 4.0
Texas 7.0
Oregon 10.0
Name: d, dtype: float64
In [148]: frame.sub(series3, axis='index')
Out[148]:
b d e
Utah -1.0 0.0 1.0
Ohio -1.0 0.0 1.0
Texas -1.0 0.0 1.0
Oregon -1.0 0.0 1.0
Универсальные функции NumPy также работают с объектами pandas
:
In [149]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
...: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [150]: frame
Out[150]:
b d e
Utah -0.373512 -0.408758 -0.865501
Ohio 0.644210 -1.974533 0.827712
Texas -0.002276 -0.452517 0.904933
Oregon -0.458469 0.526754 -1.517036
In [151]: np.abs(frame)
Out[151]:
b d e
Utah 0.373512 0.408758 0.865501
Ohio 0.644210 1.974533 0.827712
Texas 0.002276 0.452517 0.904933
Oregon 0.458469 0.526754 1.517036
Другой частой операцией является применение функции к одномерным
массивам для каждого столбца или строки. Метод apply
объекта
DataFrame
выполняет это:
In [152]: f = lambda x: x.max() - x.min()
In [153]: frame.apply(f)
Out[153]:
b 1.102679
d 2.501287
e 2.421969
dtype: float64
В результате мы получили объект Series
, у которого индекс совпадает
со столбцами объекта DataFrame
.
Если задать параметр axis = 'columns'
в функции apply
, функция
будет применяться к строкам:
In [154]: frame.apply(f, axis='columns')
Out[154]:
Utah 0.491989
Ohio 2.802245
Texas 1.357449
Oregon 2.043790
dtype: float64
Многие из наиболее распространенных статистических методов
(например, sum
и mean
) являются методами DataFrame
, поэтому
использование apply
не обязательно.
Функция, передаваемая в apply
, не обязана возвращать скалярное
значение, она может также возвращать объект Series
:
In [155]: frame
Out[155]:
b d e
Utah 0.485727 0.656970 0.799615
Ohio 1.296945 -0.736579 0.243545
Texas -0.953977 -1.293465 0.154692
Oregon -0.270480 0.684838 1.046487
In [156]: def f(x):
...: return pd.Series([x.min(), x.max()], index=['min', 'max'])
In [157]: frame.apply(f)
Out[157]:
b d e
min -0.953977 -1.293465 0.154692
max 1.296945 0.684838 1.046487
Также можно использовать поэлементные функции. Предположим, нужно
получить форматированную строку для каждого значения в объекте
frame
. Это можно реализовать с помощью функции applymap
:
In [158]: format = lambda x: '%.2f' % x
In [159]: frame.applymap(format)
Out[159]:
b d e
Utah 0.49 0.66 0.80
Ohio 1.30 -0.74 0.24
Texas -0.95 -1.29 0.15
Oregon -0.27 0.68 1.05
В функции applymap
используется метод map
класса Series
:
In [160]: frame['e'].map(format)
Out[160]:
Utah 0.80
Ohio 0.24
Texas 0.15
Oregon 1.05
Name: e, dtype: object
Одна из важных встроенных операций — это сортировка данных. Для того,
чтобы выполнить лексикографическую сортировку по индексам строк или
столбцов, можно использовать функцию sort_index
, которая возвращает
новый отсортированный объект:
In [161]: obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
In [162]: obj.sort_index()
Out[162]:
a 1
b 2
c 3
d 0
dtype: int64
Объект DataFrame
можно сортировать по индексам на любой оси:
In [163]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
...: index=['three', 'one'],
...: columns=['d', 'a', 'b', 'c'])
In [164]: frame.sort_index()
Out[164]:
d a b c
one 4 5 6 7
three 0 1 2 3
In [165]: frame.sort_index(axis=1)
Out[165]:
a b c d
three 1 2 3 0
one 5 6 7 4
Данные сортируются по возрастанию по умолчанию, но могут быть отсортированы также по убыванию:
In [166]: frame.sort_index(axis=1, ascending=False)
Out[166]:
d c b a
three 0 3 2 1
one 4 7 6 5
Для сортировки объекта Series
по значениям используется метод
sort_values
:
In [167]: obj = pd.Series([4, 7, -3, 2])
In [168]: obj.sort_values()
Out[168]:
2 -3
3 2
0 4
1 7
dtype: int64
Все пропущенные значения по умолчанию сортируются в конец объекта
Series
:
In [169]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
In [170]: obj.sort_values()
Out[170]:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
При сортировке объекта DataFrame
можно использовать данные в одном
или нескольких столбцах в качестве ключей для сортировки. Чтобы
выполнить это, необходимо передать имя одного или нескольких столбцов
параметру by
метода sort_vlues
:
In [171]: frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
In [172]: frame
Out[172]:
b a
0 4 0
1 7 1
2 -3 0
3 2 1
In [173]: frame.sort_values(by='b')
Out[173]:
b a
2 -3 0
3 2 1
0 4 0
1 7 1
Для сортировки по нескольким столбцам, необходимо передать список имен столбцов:
In [174]: frame.sort_values(by=['a', 'b'])
Out[174]:
b a
2 -3 0
0 4 0
3 2 1
1 7 1
Ранжирование заключается в присвоении ранга от единицы до числа значений в
массиве. Объекты Series
и DataFrame
имеют метод rank
, который по
умолчанию разрывает связи, присваивая каждой группе среднее значение
ранга:
In [175]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
In [176]: obj.rank()
Out[176]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
Ранги также могут быть назначены в соответствии с порядком, в котором они наблюдаются в данных:
In [177]: obj.rank(method='first')
Out[177]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
Здесь вместо использования среднего ранга \( 6.5 \) для записей с
индексами 0
и 2
они вместо этого были установлены на \( 6 \) и \( 7 \),
потому что метка \( 0 \) предшествует метке \( 2 \) в данных.
В таблице pandas:functionality:tbl:3 представлен перечень методов построения ранга.
Объект DataFrame
может вычислять ранги по строкам или по столбцам:
In [178]: frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
...: 'c': [-2, 5, 8, -2.5]})
In [179]: frame
Out[179]:
b a c
0 4.3 0 -2.0
1 7.0 1 5.0
2 -3.0 0 8.0
3 2.0 1 -2.5
In [180]: frame.rank(axis='columns')
Out[180]:
b a c
0 3.0 2.0 1.0
1 3.0 1.0 2.0
2 1.0 2.0 3.0
3 3.0 2.0 1.0
Таблица 5. Методы ранжирования
Метод | Описание |
average | Используется по умолчанию. Присваивает среднее значение ранга каждому значению в группе |
min | Использует минимальный ранг для всей группы |
max | Использует максимальный ранг для всей группы |
first | Присваивает ранги в порядке появления значений в данных |
dense | Как method = 'min' , но ранги между группами всегда увеличиваются на 1, а не на количество равных элементов в группе |
В рассматриваемых выше примерах индексы имели единственные значения,
без повторений. Хотя многие функции библиотеки pandas
(например,
reindex
) требуют, чтобы метки были уникальными, это не
обязательно. Рассмотрим ряд с повторяющимися индексами:
In [181]: obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
In [182]: obj
Out[182]:
a 0
a 1
b 2
b 3
c 4
dtype: int64
У объекта Index
есть атрибут is_unique
, который дает информацию
являются ли метки индекса уникальными:
In [183]: obj.index.is_unique
Out[183]: False
В случае, когда несколько данных имеют одинаковые метки, обращение по
этому индексу вернет объект Series
, в то время как для меток без
дублирования возвращается скалярное значение:
In [184]: obj['a']
Out[184]:
a 0
a 1
dtype: int64
In [185]: obj['c']
Out[185]: 4
Та же логика распространяется и на индексирование строк в DataFrame
:
In [186]: df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
In [187]: df
Out[187]:
0 1 2
a -0.131446 1.837088 -0.618983
a 0.505283 1.940973 0.619603
b 0.209986 0.225563 0.507683
b -0.763314 0.380339 1.837083
In [188]: df.loc['b']
Out[188]:
0 1 2
b 0.209986 0.225563 0.507683
b -0.763314 0.380339 1.837083