Вступление в структуры данных
Мы начнем с краткого, неполного обзора основных структур данных в pandas, чтобы вы могли начать работу. Основные особенности типов данных, индексации, маркировки осей и выравнивания применимы ко всем объектам. Для начала импортируйте NumPy и загрузите pandas в ваше пространство имен:
In [1]: import numpy as np
In [2]: import pandas as pd
В основе своей, выравнивание данных является неотъемлемым. Связь между метками и данными не будет нарушена, если вы этого явно не сделаете.
Мы дадим краткое введение в структуры данных, а затем рассмотрим все широкие категории функциональности и методов в отдельных разделах.
Серия
Series — это одномерный маркированный массив, способный хранить любые типы данных (целые числа, строки, числа с плавающей точкой, объекты Python и т.д.). Метки осей вместе называются индексом. Основной метод создания Series заключается в вызове:
>>> s = pd.Series(data, index=index)
Здесь data может быть многим:
словарём Python
массивом ndarray
скалярным значением (например, 5)
Переданный индекс — это список меток осей. Таким образом, это разделяется на несколько случаев в зависимости от того, что представляет собой данные:
Из ndarray
Если data является ndarray, индекс должен иметь такую же длину, как и данные. Если индекс не передан, он будет создан со значениями [0, ..., len(data) - 1].
In [3]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [4]: s
Out[4]:
a 0.469112
b -0.282863
c -1.509059
d -1.135632
e 1.212112
dtype: float64
In [5]: s.index
Out[5]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
In [6]: pd.Series(np.random.randn(5))
Out[6]:
0 -0.173215
1 0.119209
2 -1.044236
3 -0.861849
4 -2.104569
dtype: float64
Примечание
pandas поддерживает не уникальные значения индекса. Если будет попытка операции, которая не поддерживает дублирующие значения индекса, будет возбуждено исключение.
Из словаря
Series можно создать из словарей:
In [7]: d = {"b": 1, "a": 0, "c": 2}
In [8]: pd.Series(d)
Out[8]:
b 1
a 0
c 2
dtype: int64
Если передан индекс, будут извлечены значения в данных, соответствующие меткам в индексе.
In [9]: d = {"a": 0.0, "b": 1.0, "c": 2.0}
In [10]: pd.Series(d)
Out[10]:
a 0.0
b 1.0
c 2.0
dtype: float64
In [11]: pd.Series(d, index=["b", "c", "d", "a"])
Out[11]:
b 1.0
c 2.0
d NaN
a 0.0
dtype: float64
Примечание
NaN (не число) — это стандартный маркер отсутствующих данных, используемый в pandas.
Из скалярного значения
Если data является скалярным значением, должен быть передан индекс. Значение будет повторяться, чтобы соответствовать длине индекса.
In [12]: pd.Series(5.0, index=["a", "b", "c", "d", "e"])
Out[12]:
a 5.0
b 5.0
c 5.0
d 5.0
e 5.0
dtype: float64
Серия похожа на ndarray
Series ведет себя очень похоже на ndarray, и является допустимым аргументом для большинства функций NumPy. Однако операции, такие как срезы, также будут срезать индекс.
In [13]: s[0]
Out[13]: 0.4691122999071863
In [14]: s[:3]
Out[14]:
a 0.469112
b -0.282863
c -1.509059
dtype: float64
In [15]: s[s > s.median()]
Out[15]:
a 0.469112
e 1.212112
dtype: float64
In [16]: s[[4, 3, 1]]
Out[16]:
e 1.212112
d -1.135632
b -0.282863
dtype: float64
In [17]: np.exp(s)
Out[17]:
a 1.598575
b 0.753623
c 0.221118
d 0.321219
e 3.360575
dtype: float64
Примечание
Мы рассмотрим индексирование на основе массивов, например, s[[4, 3, 1]], в разделе по индексированию.
Как и массив NumPy, pandas Series имеет один dtype.
In [18]: s.dtype
Out[18]: dtype('float64')
Часто это тип данных NumPy. Однако, pandas и сторонние библиотеки расширяют систему типов NumPy в некоторых местах, в этом случае тип данных будет ExtensionDtype. Некоторые примеры внутри pandas — это Категориальные данные и Тип данных целых чисел с Nullable. Подробнее см. dtypes.
Если вам нужен фактический массив, который лежит в основе Series, используйте Series.array.
In [19]: s.array
Out[19]:
<PandasArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
-1.1356323710171934, 1.2121120250208506]
Length: 5, dtype: float64
Доступ к массиву может быть полезен, когда вам нужно выполнить какую-либо операцию без индекса (чтобы отключить, например, автоматическое выравнивание).
Series.array всегда будет ExtensionArray. Вкратце, ExtensionArray — это тонкая оболочка вокруг одного или нескольких конкретных массивов, таких как numpy.ndarray. pandas знает, как взять ExtensionArray и сохранить его в Series или столбце DataFrame. Подробнее см. dtypes.
Хотя Series похож на ndarray, если вам нужен фактический ndarray, используйте Series.to_numpy().
In [20]: s.to_numpy()
Out[20]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
Даже если Series поддерживается ExtensionArray, Series.to_numpy() вернёт NumPy ndarray.
Серия похожа на словарь
Серия также похожа на словарь фиксированного размера, так как вы можете получать и устанавливать значения по метке индекса:
In [21]: s["a"]
Out[21]: 0.4691122999071863
In [22]: s["e"] = 12.0
In [23]: s
Out[23]:
a 0.469112
b -0.282863
c -1.509059
d -1.135632
e 12.000000
dtype: float64
In [24]: "e" in s
Out[24]: True
In [25]: "f" in s
Out[25]: False
Если метка не содержится в индексе, возбуждается исключение:
In [26]: s["f"]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/core/indexes/base.py:3800, in Index.get_loc(self, key, method, tolerance)
3799 try:
-> 3800 return self._engine.get_loc(casted_key)
3801 except KeyError as err:
File ~/work/pandas/pandas/pandas/_libs/index.pyx:138, in pandas._libs.index.IndexEngine.get_loc()
File ~/work/pandas/pandas/pandas/_libs/index.pyx:165, in pandas._libs.index.IndexEngine.get_loc()
File ~/work/pandas/pandas/pandas/_libs/hashtable_class_helper.pxi:5745, in pandas._libs.hashtable.PyObjectHashTable.get_item()
File ~/work/pandas/pandas/pandas/_libs/hashtable_class_helper.pxi:5753, in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'f'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
Cell In [26], line 1
----> 1 s["f"]
File ~/work/pandas/pandas/pandas/core/series.py:982, in Series.__getitem__(self, key)
979 return self._values[key]
981 elif key_is_scalar:
--> 982 return self._get_value(key)
984 if is_hashable(key):
985 # Otherwise index.get_value will raise InvalidIndexError
986 try:
987 # For labels that don't resolve as scalars like tuples and frozensets
File ~/work/pandas/pandas/pandas/core/series.py:1092, in Series._get_value(self, label, takeable)
1089 return self._values[label]
1091 # Similar to Index.get_value, but we do not fall back to positional
-> 1092 loc = self.index.get_loc(label)
1093 return self.index._get_values_for_loc(self, loc, label)
File ~/work/pandas/pandas/pandas/core/indexes/base.py:3802, in Index.get_loc(self, key, method, tolerance)
3800 return self._engine.get_loc(casted_key)
3801 except KeyError as err:
-> 3802 raise KeyError(key) from err
3803 except TypeError:
3804 # If we have a listlike key, _check_indexing_error will raise
3805 # InvalidIndexError. Otherwise we fall through and re-raise
3806 # the TypeError.
3807 self._check_indexing_error(key)
KeyError: 'f'
Используя метод Series.get(), отсутствующая метка вернёт None или указанное значение по умолчанию:
In [27]: s.get("f")
In [28]: s.get("f", np.nan)
Out[28]: nan
Эти метки также можно получить с помощью атрибутов.
Векторные операции и выравнивание меток с Series
При работе с сырыми массивами NumPy, обычно нет необходимости в прохождении по значениям в цикле. То же самое верно и при работе с Series в pandas. Series также можно передать в большинство методов NumPy, ожидающих ndarray.
In [29]: s + s
Out[29]:
a 0.938225
b -0.565727
c -3.018117
d -2.271265
e 24.000000
dtype: float64
In [30]: s * 2
Out[30]:
a 0.938225
b -0.565727
c -3.018117
d -2.271265
e 24.000000
dtype: float64
In [31]: np.exp(s)
Out[31]:
a 1.598575
b 0.753623
c 0.221118
d 0.321219
e 162754.791419
dtype: float64
Ключевое отличие между Series и ndarray заключается в том, что операции между Series автоматически выравнивают данные на основе меток. Таким образом, можно выполнять вычисления, не задумываясь о том, имеют ли участвующие Series одинаковые метки.
In [32]: s[1:] + s[:-1]
Out[32]:
a NaN
b -0.565727
c -3.018117
d -2.271265
e NaN
dtype: float64
Результат операции между невыровненными Series будет иметь объединение индексов, участвующих в операции. Если метка не найдена в одном Series или другом, результат будет помечен как отсутствующий NaN. Возможность писать код без явного выравнивания данных обеспечивает огромную свободу и гибкость в интерактивном анализе и исследовании данных. Интегрированные функции выравнивания данных в структурах данных pandas выделяют pandas среди большинства аналогичных инструментов для работы с маркированными данными.
Примечание
В целом, мы выбрали в качестве значения по умолчанию результат операций между объектами с разными индексами — объединение индексов, чтобы избежать потери информации. Наличие метки индекса, хотя данные отсутствуют, обычно является важной информацией в рамках вычисления. Вы, конечно, можете выбрать удаление меток с отсутствующими данными с помощью функции dropna.
Атрибут имени
Series также имеет атрибут name:
In [33]: s = pd.Series(np.random.randn(5), name="something")
In [34]: s
Out[34]:
0 -0.494929
1 1.071804
2 0.721555
3 -0.706771
4 -1.039575
Name: something, dtype: float64
In [35]: s.name
Out[35]: 'something'
У Series name может быть присвоено автоматически во многих случаях, в частности, при выборе одного столбца из DataFrame, name будет присвоено имя столбца.
Вы можете переименовать Series с помощью метода pandas.Series.rename().
In [36]: s2 = s.rename("different")
In [37]: s2.name
Out[37]: 'different'
Обратите внимание, что s и s2 относятся к разным объектам.
DataFrame
DataFrame — это двумерная помеченная структура данных со столбцами потенциально разных типов. Вы можете представить её как электронную таблицу или таблицу SQL, или как словарь объектов Series. Это, как правило, наиболее часто используемый объект pandas. Как и Series, DataFrame принимает множество различных типов входных данных:
Словарь 1D массивов ndarray, списков, словарей или
Series2-мерный массив numpy.ndarray
Структурированный или записьной массив ndarray
Другой
DataFrame
Наряду с данными, вы можете необязательно передать аргументы индекс (метки строк) и столбцы (метки столбцов). Если вы передадите индекс и/или столбцы, вы гарантируете индекс и/или столбцы результирующего DataFrame. Таким образом, словарь Series плюс определённый индекс отбросит все данные, которые не соответствуют переданному индексу.
Если метки осей не переданы, они будут сконструированы из входных данных на основе общих правил.
Из словаря Series или словарей
Результирующий индекс будет объединением индексов различных Series. Если есть вложенные словари, они сначала преобразуются в Series. Если столбцы не переданы, столбцы будут упорядоченным списком ключей словаря.
In [38]: d = {
....: "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
....: "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
....: }
....:
In [39]: df = pd.DataFrame(d)
In [40]: df
Out[40]:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In [41]: pd.DataFrame(d, index=["d", "b", "a"])
Out[41]:
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
In [42]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[42]:
two three
d 4.0 NaN
b 2.0 NaN
a 1.0 NaN
Метки строк и столбцов можно получить соответственно, обратившись к атрибутам индекс и столбцы:
Примечание
Когда определённый набор столбцов передаётся вместе со словарем данных, переданные столбцы переопределяют ключи в словаре.
In [43]: df.index
Out[43]: Index(['a', 'b', 'c', 'd'], dtype='object')
In [44]: df.columns
Out[44]: Index(['one', 'two'], dtype='object')
Из словаря ndarray/списков
Массивы ndarray должны иметь одинаковую длину. Если передан индекс, его длина также должна совпадать с длиной массивов. Если индекс не передан, результатом будет range(n), где n — длина массива.
In [45]: d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}
In [46]: pd.DataFrame(d)
Out[46]:
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
In [47]: pd.DataFrame(d, index=["a", "b", "c", "d"])
Out[47]:
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
Из структурированного или записьного массива
Этот случай обрабатывается идентично словарю массивов.
In [48]: data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])
In [49]: data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]
In [50]: pd.DataFrame(data)
Out[50]:
A B C
0 1 2.0 b'Hello'
1 2 3.0 b'World'
In [51]: pd.DataFrame(data, index=["first", "second"])
Out[51]:
A B C
first 1 2.0 b'Hello'
second 2 3.0 b'World'
In [52]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[52]:
C A B
0 b'Hello' 1 2.0
1 b'World' 2 3.0
Примечание
DataFrame не предназначен для работы точно так же, как двумерный массив NumPy.
Из списка словарей
In [53]: data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
In [54]: pd.DataFrame(data2)
Out[54]:
a b c
0 1 2 NaN
1 5 10 20.0
In [55]: pd.DataFrame(data2, index=["first", "second"])
Out[55]:
a b c
first 1 2 NaN
second 5 10 20.0
In [56]: pd.DataFrame(data2, columns=["a", "b"])
Out[56]:
a b
0 1 2
1 5 10
Из словаря кортежей
Вы можете автоматически создать фрейм с многоуровневым индексом, передав словарь кортежей.
In [57]: pd.DataFrame(
....: {
....: ("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
....: ("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
....: ("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
....: ("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
....: ("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
....: }
....: )
....:
Out[57]:
a b
b a c a b
A B 1.0 4.0 5.0 8.0 10.0
C 2.0 3.0 6.0 7.0 NaN
D NaN NaN NaN NaN 9.0
Из Series
Результатом будет DataFrame с тем же индексом, что и входной Series, и с одним столбцом, имя которого — исходное имя Series (только если не указано другое имя столбца).
In [58]: ser = pd.Series(range(3), index=list("abc"), name="ser")
In [59]: pd.DataFrame(ser)
Out[59]:
ser
a 0
b 1
c 2
Из списка именованных кортежей
Имена полей первого namedtuple в списке определяют столбцы DataFrame. Остальные именованные кортежи (или кортежи) просто распаковываются, и их значения передаются в строки DataFrame. Если какой-либо из этих кортежей короче первого namedtuple, то последующие столбцы в соответствующей строке отмечаются как пропущенные значения. Если какие-либо из них длиннее первого namedtuple, то возбуждается ValueError.
In [60]: from collections import namedtuple
In [61]: Point = namedtuple("Point", "x y")
In [62]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[62]:
x y
0 0 0
1 0 3
2 2 3
In [63]: Point3D = namedtuple("Point3D", "x y z")
In [64]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[64]:
x y z
0 0 0 0.0
1 0 3 5.0
2 2 3 NaN
Из списка DataClasses
Новое в версии 1.1.0.
Data Class, представленные в PEP557, могут быть переданы в конструктор DataFrame. Передача списка DataClasses эквивалентна передаче списка словарей.
Обратите внимание, что все значения в списке должны быть DataClasses, смешивание типов в списке приведёт к TypeError.
In [65]: from dataclasses import make_dataclass
In [66]: Point = make_dataclass("Point", [("x", int), ("y", int)])
In [67]: pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
Out[67]:
x y
0 0 0
1 0 3
2 2 3
Пропущенные данные
Для создания DataFrame с пропущенными данными мы используем np.nan для представления пропущенных значений. Также вы можете передать numpy.MaskedArray в качестве аргумента данных конструктору DataFrame, и его замаскированные элементы будут рассматриваться как пропущенные. Подробнее см. Пропущенные данные.
Альтернативные конструкторы
DataFrame.from_dict
DataFrame.from_dict() принимает словарь словарей или словарь массивоподобных последовательностей и возвращает DataFrame. Он работает как конструктор DataFrame, за исключением параметра orient, который равен 'columns' по умолчанию, но который можно установить в 'index' для использования ключей словаря в качестве меток строк.
In [68]: pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
Out[68]:
A B
0 1 4
1 2 5
2 3 6
Если вы передаёте orient='index', ключи будут метками строк. В этом случае вы также можете передать желаемые имена столбцов:
In [69]: pd.DataFrame.from_dict(
....: dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
....: orient="index",
....: columns=["one", "two", "three"],
....: )
....:
Out[69]:
one two three
A 1 2 3
B 4 5 6
DataFrame.from_records
DataFrame.from_records() принимает список кортежей или массив ndarray со структурированным типом. Он работает аналогично обычному конструктору DataFrame, за исключением того, что индекс результирующего DataFrame может быть определённым полем структурированного типа.
In [70]: data
Out[70]:
array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In [71]: pd.DataFrame.from_records(data, index="C")
Out[71]:
A B
C
b'Hello' 1 2.0
b'World' 2 3.0
Выбор, добавление и удаление столбцов
Вы можете интерпретировать DataFrame как словарь подобных индексированных объектов Series. Получение, установка и удаление столбцов работает с той же синтаксической конструкцией, что и аналогичные операции со словарями:
In [72]: df["one"]
Out[72]:
a 1.0
b 2.0
c 3.0
d NaN
Name: one, dtype: float64
In [73]: df["three"] = df["one"] * df["two"]
In [74]: df["flag"] = df["one"] > 2
In [75]: df
Out[75]:
one two three flag
a 1.0 1.0 1.0 False
b 2.0 2.0 4.0 False
c 3.0 3.0 9.0 True
d NaN 4.0 NaN False
Столбцы можно удалить или извлечь, как и со словарями:
In [76]: del df["two"]
In [77]: three = df.pop("three")
In [78]: df
Out[78]:
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
При вставке скалярного значения, он будет естественным образом распространяться, чтобы заполнить столбец:
In [79]: df["foo"] = "bar"
In [80]: df
Out[80]:
one flag foo
a 1.0 False bar
b 2.0 False bar
c 3.0 True bar
d NaN False bar
При вставке Series, который не имеет того же индекса, что и DataFrame, он будет согласован с индексом DataFrame:
In [81]: df["one_trunc"] = df["one"][:2]
In [82]: df
Out[82]:
one flag foo one_trunc
a 1.0 False bar 1.0
b 2.0 False bar 2.0
c 3.0 True bar NaN
d NaN False bar NaN
Вы можете вставлять сырые массивы ndarray, но их длина должна совпадать с длиной индекса DataFrame.
По умолчанию столбцы вставляются в конец. DataFrame.insert() вставляет в конкретное место в столбцах:
In [83]: df.insert(1, "bar", df["one"])
In [84]: df
Out[84]:
one bar flag foo one_trunc
a 1.0 1.0 False bar 1.0
b 2.0 2.0 False bar 2.0
c 3.0 3.0 True bar NaN
d NaN NaN False bar NaN
Назначение новых столбцов в цепочках методов
Вдохновленный методом dplyr mutate DataFrame имеет метод assign(), который позволяет легко создавать новые столбцы, потенциально производные от существующих.
In [85]: iris = pd.read_csv("data/iris.data")
In [86]: iris.head()
Out[86]:
SepalLength SepalWidth PetalLength PetalWidth Name
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
In [87]: iris.assign(sepal_ratio=iris["SepalWidth"] / iris["SepalLength"]).head()
Out[87]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
В примере выше мы вставили предварительно вычисленное значение. Мы также можем передать функцию от одного аргумента, которая должна быть вычислена для DataFrame, которому назначается столбец.
In [88]: iris.assign(sepal_ratio=lambda x: (x["SepalWidth"] / x["SepalLength"])).head()
Out[88]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
assign() всегда возвращает копию данных, оставляя исходный DataFrame без изменений.
Передача вызываемого объекта вместо фактического значения для вставки полезна, когда у вас нет ссылки на DataFrame под рукой. Это часто используется при использовании assign() в цепочке операций. Например, мы можем ограничить DataFrame только теми наблюдениями, у которых длина Sepal больше 5, вычислить отношение и отобразить его:
In [89]: (
....: iris.query("SepalLength > 5")
....: .assign(
....: SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
....: PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
....: )
....: .plot(kind="scatter", x="SepalRatio", y="PetalRatio")
....: )
....:
Out[89]: <AxesSubplot: xlabel='SepalRatio', ylabel='PetalRatio'>
Поскольку передаётся функция, функция вычисляется для DataFrame, которому присваивается столбец. Важно, что это DataFrame, который был отфильтрован до тех строк, у которых длина сепалы больше 5. Фильтрация происходит первой, а затем вычисления отношения. Это пример, в котором у нас не было ссылки на отфильтрованный DataFrame.
Подпись функции для assign() просто **kwargs. Ключи — это имена столбцов для новых полей, а значения — либо значение для вставки (например, Series или массив NumPy), либо функция от одного аргумента, которая вызывается для DataFrame. Возвращается копия исходного DataFrame со вставленными новыми значениями.
Порядок **kwargs сохраняется. Это позволяет для зависимого назначения, где выражение позже в **kwargs может ссылаться на столбец, созданный ранее в той же цепочке вызовов assign().
In [90]: dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
In [91]: dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
Out[91]:
A B C D
0 1 4 5 6
1 2 5 7 9
2 3 6 9 12
Во втором выражении x['C'] будет ссылаться на новый созданный столбец, равный dfa['A'] + dfa['B'].
Индексирование / выбор
Основы индексирования следующие:
Операция | Синтаксис | Результат |
|---|---|---|
Выбрать столбец |
| Серия |
Выбрать строку по метке |
| Серия |
Выбрать строку по целочисленному местоположению |
| Серия |
Выбрать срезы строк |
| DataFrame |
Выбрать строки по булевому вектору |
| DataFrame |
Выбор строки, например, возвращает Series, индекс которой является столбцами DataFrame:
In [92]: df.loc["b"]
Out[92]:
one 2.0
bar 2.0
flag False
foo bar
one_trunc 2.0
Name: b, dtype: object
In [93]: df.iloc[2]
Out[93]:
one 3.0
bar 3.0
flag True
foo bar
one_trunc NaN
Name: c, dtype: object
Для более подробного рассмотрения сложного индексирования и срезов по меткам см. раздел по индексированию. Мы рассмотрим основы повторной индексации/соответствия новым наборам меток в разделе по повторной индексации.
Выравнивание данных и арифметика
Выравнивание данных между объектами DataFrame автоматически выравнивается по столбцам и индексу (меткам строк). Результирующий объект будет иметь объединение меток столбцов и строк.
In [94]: df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])
In [95]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])
In [96]: df + df2
Out[96]:
A B C D
0 0.045691 -0.014138 1.380871 NaN
1 -0.955398 -1.501007 0.037181 NaN
2 -0.662690 1.534833 -0.859691 NaN
3 -2.452949 1.237274 -0.133712 NaN
4 1.414490 1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
При выполнении операции между DataFrame и Series, поведение по умолчанию заключается в выравнивании Series индекса по DataFrame столбцам, таким образом, происходит векторное вычисление по строкам. Например:
In [97]: df - df.iloc[0]
Out[97]:
A B C D
0 0.000000 0.000000 0.000000 0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2 0.253128 0.829678 0.010026 -1.991234
3 -1.311128 0.054325 -1.724913 -1.620544
4 0.573025 1.500742 -0.676070 1.367331
5 -1.741248 0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282 0.000430
7 -0.743894 0.411013 -0.929563 -0.282386
8 -1.194921 1.320690 0.238224 -1.482644
9 2.293786 1.856228 0.773289 -1.446531
Для явного управления поведением соответствия и векторизации см. раздел гибких бинарных операций.
Арифметические операции со скалярами выполняются поэлементно:
In [98]: df * 5 + 2
Out[98]:
A B C D
0 3.359299 -0.124862 4.835102 3.381160
1 -3.437003 -1.368449 2.568242 -5.392133
2 4.624938 4.023526 4.885230 -6.575010
3 -3.196342 0.146766 -3.789461 -4.721559
4 6.224426 7.378849 1.454750 10.217815
5 -5.346940 3.785103 -1.373001 -6.884519
6 -2.844569 -4.472618 4.068691 3.383309
7 -0.360173 1.930201 0.187285 1.969232
8 -2.615303 6.478587 6.026220 -4.032059
9 14.828230 9.156280 8.701544 -3.851494
In [99]: 1 / df
Out[99]:
A B C D
0 3.678365 -2.353094 1.763605 3.620145
1 -0.919624 -1.484363 8.799067 -0.676395
2 1.904807 2.470934 1.732964 -0.583090
3 -0.962215 -2.697986 -0.863638 -0.743875
4 1.183593 0.929567 -9.170108 0.608434
5 -0.680555 2.800959 -1.482360 -0.562777
6 -1.032084 -0.772485 2.416988 3.614523
7 -2.118489 -71.634509 -2.758294 -162.507295
8 -1.083352 1.116424 1.241860 -0.828904
9 0.389765 0.698687 0.746097 -0.854483
In [100]: df ** 4
Out[100]:
A B C D
0 0.005462 3.261689e-02 0.103370 5.822320e-03
1 1.398165 2.059869e-01 0.000167 4.777482e+00
2 0.075962 2.682596e-02 0.110877 8.650845e+00
3 1.166571 1.887302e-02 1.797515 3.265879e+00
4 0.509555 1.339298e+00 0.000141 7.297019e+00
5 4.661717 1.624699e-02 0.207103 9.969092e+00
6 0.881334 2.808277e+00 0.029302 5.858632e-03
7 0.049647 3.797614e-08 0.017276 1.433866e-09
8 0.725974 6.437005e-01 0.420446 2.118275e+00
9 43.329821 4.196326e+00 3.227153 1.875802e+00
Булевы операторы также работают поэлементно:
In [101]: df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)
In [102]: df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)
In [103]: df1 & df2
Out[103]:
a b
0 False False
1 False True
2 True False
In [104]: df1 | df2
Out[104]:
a b
0 True True
1 True True
2 True True
In [105]: df1 ^ df2
Out[105]:
a b
0 True True
1 True False
2 False True
In [106]: -df1
Out[106]:
a b
0 False True
1 True False
2 False False
Транспонирование
Для транспонирования используйте атрибут T или метод DataFrame.transpose(), аналогично ndarray:
# only show the first 5 rows
In [107]: df[:5].T
Out[107]:
0 1 2 3 4
A 0.271860 -1.087401 0.524988 -1.039268 0.844885
B -0.424972 -0.673690 0.404705 -0.370647 1.075770
C 0.567020 0.113648 0.577046 -1.157892 -0.109050
D 0.276232 -1.478427 -1.715002 -1.344312 1.643563
Взаимодействие DataFrame с функциями NumPy
Большинство функций NumPy могут быть вызваны непосредственно для Series и DataFrame.
In [108]: np.exp(df)
Out[108]:
A B C D
0 1.312403 0.653788 1.763006 1.318154
1 0.337092 0.509824 1.120358 0.227996
2 1.690438 1.498861 1.780770 0.179963
3 0.353713 0.690288 0.314148 0.260719
4 2.327710 2.932249 0.896686 5.173571
5 0.230066 1.429065 0.509360 0.169161
6 0.379495 0.274028 1.512461 1.318720
7 0.623732 0.986137 0.695904 0.993865
8 0.397301 2.449092 2.237242 0.299269
9 13.009059 4.183951 3.820223 0.310274
In [109]: np.asarray(df)
Out[109]:
array([[ 0.2719, -0.425 , 0.567 , 0.2762],
[-1.0874, -0.6737, 0.1136, -1.4784],
[ 0.525 , 0.4047, 0.577 , -1.715 ],
[-1.0393, -0.3706, -1.1579, -1.3443],
[ 0.8449, 1.0758, -0.109 , 1.6436],
[-1.4694, 0.357 , -0.6746, -1.7769],
[-0.9689, -1.2945, 0.4137, 0.2767],
[-0.472 , -0.014 , -0.3625, -0.0062],
[-0.9231, 0.8957, 0.8052, -1.2064],
[ 2.5656, 1.4313, 1.3403, -1.1703]])
DataFrame не предназначен для прямого использования вместо ndarray, так как его семантика индексирования и модель данных в некоторых местах сильно отличаются от n-мерного массива.
Series реализует __array_ufunc__, что позволяет ему работать с универсальными функциями NumPy universal functions.
Функция ufunc применяется к базовому массиву в Series.
In [110]: ser = pd.Series([1, 2, 3, 4])
In [111]: np.exp(ser)
Out[111]:
0 2.718282
1 7.389056
2 20.085537
3 54.598150
dtype: float64
Изменено в версии 0.25.0: При передаче нескольких Series в функцию ufunc, они выравниваются перед выполнением операции.
Как и в других частях библиотеки, pandas автоматически выравнивает помеченные входные данные как часть ufunc с несколькими входами. Например, использование numpy.remainder() для двух Series с различными порядками меток приведет к выравниванию перед операцией.
In [112]: ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
In [113]: ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])
In [114]: ser1
Out[114]:
a 1
b 2
c 3
dtype: int64
In [115]: ser2
Out[115]:
b 1
a 3
c 5
dtype: int64
In [116]: np.remainder(ser1, ser2)
Out[116]:
a 1
b 0
c 3
dtype: int64
Как обычно, берется объединение двух индексов, а непересекающиеся значения заполняются пропущенными значениями.
In [117]: ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])
In [118]: ser3
Out[118]:
b 2
c 4
d 6
dtype: int64
In [119]: np.remainder(ser1, ser3)
Out[119]:
a NaN
b 0.0
c 3.0
d NaN
dtype: float64
При применении бинарной функции ufunc к Series и Index, реализация Series имеет приоритет, и возвращается Series.
In [120]: ser = pd.Series([1, 2, 3])
In [121]: idx = pd.Index([4, 5, 6])
In [122]: np.maximum(ser, idx)
Out[122]:
0 4
1 5
2 6
dtype: int64
Функции NumPy ufunc безопасны для применения к Series, поддерживаемым не-ndarray массивами, например arrays.SparseArray (см. Вычисления с разреженными данными). Если это возможно, функция ufunc применяется без преобразования базовых данных в ndarray.
Вывод в консоль
Очень большой DataFrame будет усечен для отображения в консоли. Вы также можете получить сводку, используя info(). (Набор данных baseball взят из пакета plyr R):
In [123]: baseball = pd.read_csv("data/baseball.csv")
In [124]: print(baseball)
id player year stint team lg ... so ibb hbp sh sf gidp
0 88641 womacto01 2006 2 CHN NL ... 4.0 0.0 0.0 3.0 0.0 0.0
1 88643 schilcu01 2006 1 BOS AL ... 1.0 0.0 0.0 0.0 0.0 0.0
.. ... ... ... ... ... .. ... ... ... ... ... ... ...
98 89533 aloumo01 2007 1 NYN NL ... 30.0 5.0 2.0 0.0 3.0 13.0
99 89534 alomasa02 2007 1 NYN NL ... 3.0 0.0 0.0 0.0 0.0 0.0
[100 rows x 23 columns]
In [125]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 100 non-null int64
1 player 100 non-null object
2 year 100 non-null int64
3 stint 100 non-null int64
4 team 100 non-null object
5 lg 100 non-null object
6 g 100 non-null int64
7 ab 100 non-null int64
8 r 100 non-null int64
9 h 100 non-null int64
10 X2b 100 non-null int64
11 X3b 100 non-null int64
12 hr 100 non-null int64
13 rbi 100 non-null float64
14 sb 100 non-null float64
15 cs 100 non-null float64
16 bb 100 non-null int64
17 so 100 non-null float64
18 ibb 100 non-null float64
19 hbp 100 non-null float64
20 sh 100 non-null float64
21 sf 100 non-null float64
22 gidp 100 non-null float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.1+ KB
Однако, использование DataFrame.to_string() вернёт строковое представление DataFrame в табличном формате, хотя оно не всегда поместится в ширину консоли:
In [126]: print(baseball.iloc[-20:, :12].to_string())
id player year stint team lg g ab r h X2b X3b
80 89474 finlest01 2007 1 COL NL 43 94 9 17 3 0
81 89480 embreal01 2007 1 OAK AL 4 0 0 0 0 0
82 89481 edmonji01 2007 1 SLN NL 117 365 39 92 15 2
83 89482 easleda01 2007 1 NYN NL 76 193 24 54 6 0
84 89489 delgaca01 2007 1 NYN NL 139 538 71 139 30 0
85 89493 cormirh01 2007 1 CIN NL 6 0 0 0 0 0
86 89494 coninje01 2007 2 NYN NL 21 41 2 8 2 0
87 89495 coninje01 2007 1 CIN NL 80 215 23 57 11 1
88 89497 clemero02 2007 1 NYA AL 2 2 0 1 0 0
89 89498 claytro01 2007 2 BOS AL 8 6 1 0 0 0
90 89499 claytro01 2007 1 TOR AL 69 189 23 48 14 0
91 89501 cirilje01 2007 2 ARI NL 28 40 6 8 4 0
92 89502 cirilje01 2007 1 MIN AL 50 153 18 40 9 2
93 89521 bondsba01 2007 1 SFN NL 126 340 75 94 14 0
94 89523 biggicr01 2007 1 HOU NL 141 517 68 130 31 3
95 89525 benitar01 2007 2 FLO NL 34 0 0 0 0 0
96 89526 benitar01 2007 1 SFN NL 19 0 0 0 0 0
97 89530 ausmubr01 2007 1 HOU NL 117 349 38 82 16 3
98 89533 aloumo01 2007 1 NYN NL 87 328 51 112 19 1
99 89534 alomasa02 2007 1 NYN NL 8 22 1 3 1 0
Широкие DataFrame будут отображаться по нескольким строкам по умолчанию:
In [127]: pd.DataFrame(np.random.randn(3, 12))
Out[127]:
0 1 2 ... 9 10 11
0 -1.226825 0.769804 -1.281247 ... -1.110336 -0.619976 0.149748
1 -0.732339 0.687738 0.176444 ... 1.462696 -1.743161 -0.826591
2 -0.345352 1.314232 0.690579 ... 0.896171 -0.487602 -0.082240
[3 rows x 12 columns]
Вы можете изменить количество отображаемых данных в одной строке, задав опцию display.width:
In [128]: pd.set_option("display.width", 40) # default is 80
In [129]: pd.DataFrame(np.random.randn(3, 12))
Out[129]:
0 1 2 ... 9 10 11
0 -2.182937 0.380396 0.084844 ... -0.023688 2.410179 1.450520
1 0.206053 -0.251905 -2.213588 ... -0.025747 -0.988387 0.094055
2 1.262731 1.289997 0.082423 ... -0.281461 0.030711 0.109121
[3 rows x 12 columns]
Вы можете настроить максимальную ширину отдельных столбцов, задав display.max_colwidth
In [130]: datafile = {
.....: "filename": ["filename_01", "filename_02"],
.....: "path": [
.....: "media/user_name/storage/folder_01/filename_01",
.....: "media/user_name/storage/folder_02/filename_02",
.....: ],
.....: }
.....:
In [131]: pd.set_option("display.max_colwidth", 30)
In [132]: pd.DataFrame(datafile)
Out[132]:
filename path
0 filename_01 media/user_name/storage/fo...
1 filename_02 media/user_name/storage/fo...
In [133]: pd.set_option("display.max_colwidth", 100)
In [134]: pd.DataFrame(datafile)
Out[134]:
filename path
0 filename_01 media/user_name/storage/folder_01/filename_01
1 filename_02 media/user_name/storage/folder_02/filename_02
Также можно отключить эту функцию, используя опцию expand_frame_repr. Это позволит вывести таблицу в одном блоке.
Доступ к атрибутам столбцов DataFrame и завершение IPython
Если метка столбца DataFrame является допустимым именем переменной Python, к столбцу можно обратиться как к атрибуту:
In [135]: df = pd.DataFrame({"foo1": np.random.randn(5), "foo2": np.random.randn(5)})
In [136]: df
Out[136]:
foo1 foo2
0 1.126203 0.781836
1 -0.977349 -1.071357
2 1.474071 0.441153
3 -0.064034 2.353925
4 -1.282782 0.583787
In [137]: df.foo1
Out[137]:
0 1.126203
1 -0.977349
2 1.474071
3 -0.064034
4 -1.282782
Name: foo1, dtype: float64
Столбцы также связаны с механизмом завершения IPython, поэтому они могут быть завершены нажатием клавиши Tab:
In [5]: df.foo<TAB> # noqa: E225, E999
df.foo1 df.foo2
© 2008–2022, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/1.5.0/user_guide/dsintro.html