Основная базовая функциональность#
Здесь мы обсудим множество основных функций, общих для структур данных pandas. Для начала создадим несколько примеров объектов, как мы делали в 10 минут для знакомства с pandas раздел:
In [1]: index = pd.date_range("1/1/2000", periods=8)
In [2]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"])
Голова и хвост#
Чтобы просмотреть небольшую выборку объекта Series или DataFrame, используйте
head() и tail() методах. Количество отображаемых элементов по умолчанию
равно пяти, но вы можете передать пользовательское число.
In [4]: long_series = pd.Series(np.random.randn(1000))
In [5]: long_series.head()
Out[5]:
0 -1.157892
1 -1.344312
2 0.844885
3 1.075770
4 -0.109050
dtype: float64
In [6]: long_series.tail(3)
Out[6]:
997 -0.289388
998 -1.020544
999 0.589993
dtype: float64
Атрибуты и базовые данные#
Объекты pandas имеют ряд атрибутов, позволяющих получить доступ к метаданным
shape: указывает размеры оси объекта, согласованные с ndarray
- Метки осей
Series: index (только ось)
DataFrame: index (строк) и столбцы
Примечание, эти атрибуты можно безопасно присваивать!
In [7]: df[:2]
Out[7]:
A B C
2000-01-01 -0.173215 0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
In [8]: df.columns = [x.lower() for x in df.columns]
In [9]: df
Out[9]:
a b c
2000-01-01 -0.173215 0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03 1.071804 0.721555 -0.706771
2000-01-04 -1.039575 0.271860 -0.424972
2000-01-05 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427
2000-01-07 0.524988 0.404705 0.577046
2000-01-08 -1.715002 -1.039268 -0.370647
объекты pandas (Index, Series, DataFrame) можно
рассматривать как контейнеры для массивов, которые содержат фактические данные и выполняют
фактические вычисления. Для многих типов базовый массив представляет собой
numpy.ndarray. Однако pandas и сторонние библиотеки могут расширить
Система типов NumPy для добавления поддержки пользовательских массивов
(см. dtypes).
Чтобы получить фактические данные внутри Index или Series, используйте .array свойство
In [10]: s.array
Out[10]:
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
-1.1356323710171934, 1.2121120250208506]
Length: 5, dtype: float64
In [11]: s.index.array
Out[11]:
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object
array всегда будет ExtensionArray.
Точные детали того, что такое ExtensionArray это и почему pandas использует их, немного
выходит за рамки этого введения. См. dtypes подробнее.
Если вам нужен массив NumPy, используйте to_numpy()
или numpy.asarray().
In [12]: s.to_numpy()
Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
In [13]: np.asarray(s)
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
Когда серия или индекс поддерживаются
ExtensionArray, to_numpy()
может включать копирование данных и приведение значений. См. dtypes подробнее.
to_numpy() дает некоторый контроль над dtype результирующего numpy.ndarray. Например, рассмотрим даты и время с часовыми поясами.
NumPy не имеет типа данных для представления дат и времени с учётом часовых поясов, поэтому
существует два потенциально полезных представления:
Объект типа object-dtype
numpy.ndarrayсTimestampобъектов, каждый с правильнымtzA
datetime64[ns]-тип данныхnumpy.ndarray, где значения были преобразованы в UTC и часовой пояс отброшен
Часовые пояса могут быть сохранены с помощью dtype=object
In [14]: ser = pd.Series(pd.date_range("2000", periods=2, tz="CET"))
In [15]: ser.to_numpy(dtype=object)
Out[15]:
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
Timestamp('2000-01-02 00:00:00+0100', tz='CET')], dtype=object)
Или отброшено с dtype='datetime64[ns]'
In [16]: ser.to_numpy(dtype="datetime64[ns]")
Out[16]:
array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'],
dtype='datetime64[ns]')
Получение "сырых данных" внутри DataFrame возможно, немного
сложнее. Когда ваш DataFrame имеет только один тип данных для всех
столбцов, DataFrame.to_numpy() вернет исходные данные:
In [17]: df.to_numpy()
Out[17]:
array([[-0.1732, 0.1192, -1.0442],
[-0.8618, -2.1046, -0.4949],
[ 1.0718, 0.7216, -0.7068],
[-1.0396, 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]])
Если DataFrame содержит однородно типизированные данные, ndarray может быть фактически изменен на месте, и изменения отразятся в структуре данных. Для разнородных данных (например, когда некоторые столбцы DataFrame имеют не одинаковый dtype) это не будет так. Сам атрибут values, в отличие от меток осей, не может быть присвоен.
Примечание
При работе с разнородными данными тип данных результирующего ndarray будет выбран так, чтобы вместить все задействованные данные. Например, если задействованы строки, результат будет иметь тип object. Если есть только числа с плавающей точкой и целые числа, результирующий массив будет иметь тип float.
Ранее pandas рекомендовал Series.values или DataFrame.values
для извлечения данных из Series или DataFrame. Вы всё ещё найдёте ссылки
на них в старых кодовых базах и онлайн. В будущем мы рекомендуем избегать
.values и использование .array или .to_numpy(). .values имеет следующие недостатки:
Когда ваш Series содержит тип расширения, неясно, является ли
Series.valuesвозвращает массив NumPy или массив расширения.Series.arrayвсегда возвращаетExtensionArray, и никогда не копирует данные.Series.to_numpy()всегда будет возвращать массив NumPy, потенциально за счёт копирования / приведения значений.Когда ваш DataFrame содержит смесь типов данных,
DataFrame.valuesможет включать копирование данных и приведение значений к общему типу данных, относительно дорогая операция.DataFrame.to_numpy(), будучи методом, делает более понятным, что возвращаемый массив NumPy может не быть представлением тех же данных в DataFrame.
Ускоренные операции#
pandas поддерживает ускорение определенных типов бинарных числовых и логических операций с использованием numexpr Пропущенные значения, которые существовали в исходных данных,
не будут изменены. bottleneck библиотеки.
Эти библиотеки особенно полезны при работе с большими наборами данных и обеспечивают значительное ускорение. numexpr использует интеллектуальное разбиение на блоки, кэширование и несколько ядер. bottleneck представляет
собой набор специализированных cython-процедур, которые особенно быстры при работе с массивами, имеющими
nans.
Вот пример (используя 100 столбцов x 100 000 строк DataFrames):
Операция |
0.11.0 (мс) |
Предыдущая версия (мс) |
Отношение к предыдущему |
|---|---|---|---|
|
13.32 |
125.35 |
0.1063 |
|
21.71 |
36.63 |
0.5928 |
|
22.04 |
36.50 |
0.6039 |
Настоятельно рекомендуется установить обе библиотеки. См. раздел Рекомендуемые зависимости для получения дополнительной информации об установке.
Оба параметра включены по умолчанию, вы можете управлять этим, установив опции:
pd.set_option("compute.use_bottleneck", False)
pd.set_option("compute.use_numexpr", False)
Гибкие бинарные операции#
При бинарных операциях между структурами данных pandas есть два ключевых момента интереса:
Поведение вещания между объектами более высокой (например, DataFrame) и низкой размерности (например, Series).
Отсутствующие данные в вычислениях.
Мы покажем, как управлять этими проблемами независимо, хотя их можно обрабатывать одновременно.
Поведение сопоставления / трансляции#
DataFrame имеет методы add(), sub(),
mul(), div() и связанные функции
radd(), rsub(), …
для выполнения бинарных операций. Для поведения широковещания
в первую очередь интересен ввод Series. Используя эти функции, вы можете
либо сопоставлять по index или столбцы через ось ключевое слово:
In [18]: df = pd.DataFrame(
....: {
....: "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
....: "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
....: "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
....: }
....: )
....:
In [19]: df
Out[19]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [20]: row = df.iloc[1]
In [21]: column = df["two"]
In [22]: df.sub(row, axis="columns")
Out[22]:
one two three
a 1.051928 -0.139606 NaN
b 0.000000 0.000000 0.000000
c 0.352192 -0.433754 1.277825
d NaN -1.632779 -0.562782
In [23]: df.sub(row, axis=1)
Out[23]:
one two three
a 1.051928 -0.139606 NaN
b 0.000000 0.000000 0.000000
c 0.352192 -0.433754 1.277825
d NaN -1.632779 -0.562782
In [24]: df.sub(column, axis="index")
Out[24]:
one two three
a -0.377535 0.0 NaN
b -1.569069 0.0 -1.962513
c -0.783123 0.0 -0.250933
d NaN 0.0 -0.892516
In [25]: df.sub(column, axis=0)
Out[25]:
one two three
a -0.377535 0.0 NaN
b -1.569069 0.0 -1.962513
c -0.783123 0.0 -0.250933
d NaN 0.0 -0.892516
Кроме того, вы можете выровнять уровень MultiIndexed DataFrame с Series.
In [26]: dfmi = df.copy()
In [27]: dfmi.index = pd.MultiIndex.from_tuples(
....: [(1, "a"), (1, "b"), (1, "c"), (2, "a")], names=["first", "second"]
....: )
....:
In [28]: dfmi.sub(column, axis=0, level="second")
Out[28]:
one two three
first second
1 a -0.377535 0.000000 NaN
b -1.569069 0.000000 -1.962513
c -0.783123 0.000000 -0.250933
2 a NaN -1.493173 -2.385688
Series и Index также поддерживают divmod() встроенная функция. Эта функция выполняет целочисленное деление и операцию взятия остатка одновременно, возвращая кортеж из двух элементов того же типа, что и левый операнд. Например:
In [29]: s = pd.Series(np.arange(10))
In [30]: s
Out[30]:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int64
In [31]: div, rem = divmod(s, 3)
In [32]: div
Out[32]:
0 0
1 0
2 0
3 1
4 1
5 1
6 2
7 2
8 2
9 3
dtype: int64
In [33]: rem
Out[33]:
0 0
1 1
2 2
3 0
4 1
5 2
6 0
7 1
8 2
9 0
dtype: int64
In [34]: idx = pd.Index(np.arange(10))
In [35]: idx
Out[35]: Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
In [36]: div, rem = divmod(idx, 3)
In [37]: div
Out[37]: Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')
In [38]: rem
Out[38]: Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
Мы также можем выполнять поэлементные divmod():
In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])
In [40]: div
Out[40]:
0 0
1 0
2 0
3 1
4 1
5 1
6 1
7 1
8 1
9 1
dtype: int64
In [41]: rem
Out[41]:
0 0
1 1
2 2
3 0
4 0
5 1
6 1
7 2
8 2
9 3
dtype: int64
Отсутствующие данные / операции с заполнением значений#
В Series и DataFrame арифметические функции имеют возможность ввода fill_value, а именно значение для замены, когда отсутствует не более одного значения в
местоположении. Например, при сложении двух объектов DataFrame вы можете
захотеть рассматривать NaN как 0, если только оба DataFrame не содержат это значение, в этом
случае результатом будет NaN (позже вы можете заменить NaN другим значением
используя fillna если хотите).
In [42]: df2 = df.copy()
In [43]: df2.loc["a", "three"] = 1.0
In [44]: df
Out[44]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [45]: df2
Out[45]:
one two three
a 1.394981 1.772517 1.000000
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [46]: df + df2
Out[46]:
one two three
a 2.789963 3.545034 NaN
b 0.686107 3.824246 -0.100780
c 1.390491 2.956737 2.454870
d NaN 0.558688 -1.226343
In [47]: df.add(df2, fill_value=0)
Out[47]:
one two three
a 2.789963 3.545034 1.000000
b 0.686107 3.824246 -0.100780
c 1.390491 2.956737 2.454870
d NaN 0.558688 -1.226343
Гибкие сравнения#
Series и DataFrame имеют бинарные методы сравнения eq, ne, lt, gt,
le, и ge поведение которого аналогично бинарным
арифметическим операциям, описанным выше:
In [48]: df.gt(df2)
Out[48]:
one two three
a False False False
b False False False
c False False False
d False False False
In [49]: df2.ne(df)
Out[49]:
one two three
a False False True
b False False False
c False False False
d True False False
Эти операции создают объект pandas того же типа, что и входные данные левой стороны, который имеет тип данных bool. Эти boolean объекты могут использоваться в
операциях индексирования, см. раздел о Булева индексация.
Булевы редукции#
Вы можете применить редукции: empty, any(),
all(), и bool() чтобы предоставить
способ обобщения булева результата.
In [50]: (df > 0).all()
Out[50]:
one False
two True
three False
dtype: bool
In [51]: (df > 0).any()
Out[51]:
one True
two True
three True
dtype: bool
Вы можете свести к конечному логическому значению.
In [52]: (df > 0).any().any()
Out[52]: True
Вы можете проверить, пуст ли объект pandas, с помощью empty свойство.
In [53]: df.empty
Out[53]: False
In [54]: pd.DataFrame(columns=list("ABC")).empty
Out[54]: True
Предупреждение
Проверка истинности объекта pandas вызовет ошибку, так как проверка пустоты или значений неоднозначна.
In [55]: if df:
....: print(True)
....:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in ?()
----> 1 if df:
2 print(True)
~/work/pandas/pandas/pandas/core/generic.py in ?(self)
1578 @final
1579 def __nonzero__(self) -> NoReturn:
-> 1580 raise ValueError(
1581 f"The truth value of a {type(self).__name__} is ambiguous. "
1582 "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
1583 )
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
In [56]: df and df2
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in ?()
----> 1 df and df2
~/work/pandas/pandas/pandas/core/generic.py in ?(self)
1578 @final
1579 def __nonzero__(self) -> NoReturn:
-> 1580 raise ValueError(
1581 f"The truth value of a {type(self).__name__} is ambiguous. "
1582 "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
1583 )
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
См. подводные камни для более подробного обсуждения.
Сравнение эквивалентности объектов#
Часто вы можете обнаружить, что существует более одного способа вычислить один и тот же
результат. В качестве простого примера рассмотрим df + df и df * 2. Чтобы проверить,
что эти два вычисления дают одинаковый результат, используя инструменты,
представленные выше, вы можете представить использование (df + df == df * 2).all(). Но на самом деле это выражение ложно:
In [57]: df + df == df * 2
Out[57]:
one two three
a True True False
b True True True
c True True True
d False True True
In [58]: (df + df == df * 2).all()
Out[58]:
one False
two True
three False
dtype: bool
Обратите внимание, что булев DataFrame df + df == df * 2 содержит некоторые значения False!
Это происходит потому, что NaN не сравниваются как равные:
In [59]: np.nan == np.nan
Out[59]: False
Таким образом, NDFrames (такие как Series и DataFrames) имеют equals() метод для проверки равенства, где NaN в
соответствующих позициях считаются равными.
In [60]: (df + df).equals(df * 2)
Out[60]: True
Обратите внимание, что индекс Series или DataFrame должен быть в том же порядке для того, чтобы равенство было истинным:
In [61]: df1 = pd.DataFrame({"col": ["foo", 0, np.nan]})
In [62]: df2 = pd.DataFrame({"col": [np.nan, 0, "foo"]}, index=[2, 1, 0])
In [63]: df1.equals(df2)
Out[63]: False
In [64]: df1.equals(df2.sort_index())
Out[64]: True
Сравнение объектов, подобных массивам#
Вы можете удобно выполнять поэлементные сравнения при сравнении структуры данных pandas со скалярным значением:
In [65]: pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]:
0 True
1 False
2 False
dtype: bool
In [66]: pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False])
pandas также обрабатывает поэлементные сравнения между различными объектами, похожими на массивы, одинаковой длины:
In [67]: pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]:
0 True
1 True
2 False
dtype: bool
In [68]: pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]:
0 True
1 True
2 False
dtype: bool
Попытка сравнить Index или Series объекты разной длины
вызовут ValueError:
In [69]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[69], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer..new_method (self, other)
72 return NotImplemented
74 other = item_from_zerodim(other)
---> 76 return method(self, other)
File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
38 @unpack_zerodim_and_defer("__eq__")
39 def __eq__(self, other):
---> 40 return self._cmp_method(other, operator.eq)
File ~/work/pandas/pandas/pandas/core/series.py:6133, in Series._cmp_method(self, other, op)
6130 res_name = ops.get_op_result_name(self, other)
6132 if isinstance(other, Series) and not self._indexed_same(other):
-> 6133 raise ValueError("Can only compare identically-labeled Series objects")
6135 lvalues = self._values
6136 rvalues = extract_array(other, extract_numpy=True, extract_range=True)
ValueError: Can only compare identically-labeled Series objects
In [70]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[70], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer..new_method (self, other)
72 return NotImplemented
74 other = item_from_zerodim(other)
---> 76 return method(self, other)
File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
38 @unpack_zerodim_and_defer("__eq__")
39 def __eq__(self, other):
---> 40 return self._cmp_method(other, operator.eq)
File ~/work/pandas/pandas/pandas/core/series.py:6133, in Series._cmp_method(self, other, op)
6130 res_name = ops.get_op_result_name(self, other)
6132 if isinstance(other, Series) and not self._indexed_same(other):
-> 6133 raise ValueError("Can only compare identically-labeled Series objects")
6135 lvalues = self._values
6136 rvalues = extract_array(other, extract_numpy=True, extract_range=True)
ValueError: Can only compare identically-labeled Series objects
Объединение перекрывающихся наборов данных#
Иногда возникает проблема объединения двух похожих наборов данных, где значения из одного предпочтительнее значений из другого. Примером могут служить два ряда данных, представляющих определённый экономический показатель, где один считается «более качественным». Однако менее качественный ряд может иметь более длинную историю или более полное покрытие данных. Таким образом, мы хотим объединить два объекта DataFrame, где отсутствующие значения в одном DataFrame условно заполняются аналогичными помеченными значениями из другого DataFrame. Функция, реализующая эту операцию, — combine_first(),
что мы проиллюстрируем:
In [71]: df1 = pd.DataFrame(
....: {"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
....: )
....:
In [72]: df2 = pd.DataFrame(
....: {
....: "A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
....: "B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
....: }
....: )
....:
In [73]: df1
Out[73]:
A B
0 1.0 NaN
1 NaN 2.0
2 3.0 3.0
3 5.0 NaN
4 NaN 6.0
In [74]: df2
Out[74]:
A B
0 5.0 NaN
1 2.0 NaN
2 4.0 3.0
3 NaN 4.0
4 3.0 6.0
5 7.0 8.0
In [75]: df1.combine_first(df2)
Out[75]:
A B
0 1.0 NaN
1 2.0 2.0
2 3.0 3.0
3 5.0 4.0
4 3.0 6.0
5 7.0 8.0
Общее объединение DataFrame#
The combine_first() метод выше вызывает более общий
DataFrame.combine(). Этот метод принимает другой DataFrame
и функцию-комбинатор, выравнивает входной DataFrame, а затем передаёт функции-комбинатору
пары Series (т.е., столбцы с одинаковыми именами).
Так, например, чтобы воспроизвести combine_first() как указано выше:
In [76]: def combiner(x, y):
....: return np.where(pd.isna(x), y, x)
....:
In [77]: df1.combine(df2, combiner)
Out[77]:
A B
0 1.0 NaN
1 2.0 2.0
2 3.0 3.0
3 5.0 4.0
4 3.0 6.0
5 7.0 8.0
Описательная статистика#
Существует большое количество методов для вычисления описательной статистики и
других связанных операций над Series, DataFrame. Большинство из них
являются агрегациями (следовательно, производят результат меньшей размерности), такие как
sum(), mean(), и quantile(),
но некоторые из них, например cumsum() и cumprod(),
создают объект того же размера. Как правило, эти методы принимают
ось аргумент, так же как ndarray.{sum, std, …}, но ось может быть
указана по имени или целому числу:
Series: аргумент axis не требуется
DataFrame: “index” (axis=0, по умолчанию), “columns” (axis=1)
Например:
In [78]: df
Out[78]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [79]: df.mean(0)
Out[79]:
one 0.811094
two 1.360588
three 0.187958
dtype: float64
In [80]: df.mean(1)
Out[80]:
a 1.583749
b 0.734929
c 1.133683
d -0.166914
dtype: float64
Все такие методы имеют skipna параметр, указывающий, следует ли исключать отсутствующие данные (True по умолчанию):
In [81]: df.sum(0, skipna=False)
Out[81]:
one NaN
two 5.442353
three NaN
dtype: float64
In [82]: df.sum(axis=1, skipna=True)
Out[82]:
a 3.167498
b 2.204786
c 3.401050
d -0.333828
dtype: float64
В сочетании с поведением вещания/арифметики можно кратко описать различные статистические процедуры, такие как стандартизация (приведение данных к нулевому среднему и стандартному отклонению 1):
In [83]: ts_stand = (df - df.mean()) / df.std()
In [84]: ts_stand.std()
Out[84]:
one 1.0
two 1.0
three 1.0
dtype: float64
In [85]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)
In [86]: xs_stand.std(1)
Out[86]:
a 1.0
b 1.0
c 1.0
d 1.0
dtype: float64
Обратите внимание, что методы, такие как cumsum() и cumprod()
сохранить расположение NaN значения. Это несколько отличается от
expanding() и rolling() с NaN поведение дополнительно определяется min_periods параметр.
In [87]: df.cumsum()
Out[87]:
one two three
a 1.394981 1.772517 NaN
b 1.738035 3.684640 -0.050390
c 2.433281 5.163008 1.177045
d NaN 5.442353 0.563873
Вот краткая справочная таблица общих функций. Каждая также принимает необязательный level параметр, который применяется только если объект имеет
иерархический индекс.
Функция |
Описание |
|---|---|
|
Количество ненулевых наблюдений |
|
Сумма значений |
|
Среднее значение |
|
Арифметическая медиана значений |
|
Минимум |
|
Максимум |
|
Mode |
|
Абсолютное значение |
|
Произведение значений |
|
Стандартное отклонение выборки с поправкой Бесселя |
|
Несмещённая дисперсия |
|
Стандартная ошибка среднего |
|
Выборочный коэффициент асимметрии (3-й момент) |
|
Выборочный эксцесс (4-й момент) |
|
Выборочный квантиль (значение в %) |
|
Кумулятивная сумма |
|
Накопленное произведение |
|
Кумулятивный максимум |
|
Кумулятивный минимум |
Обратите внимание, что случайно некоторые методы NumPy, такие как mean, std, и sum,
будет исключать NA для ввода Series по умолчанию:
In [88]: np.mean(df["one"])
Out[88]: 0.8110935116651192
In [89]: np.mean(df["one"].to_numpy())
Out[89]: nan
Series.nunique() вернет количество уникальных не-NA значений в
Series:
In [90]: series = pd.Series(np.random.randn(500))
In [91]: series[20:500] = np.nan
In [92]: series[10:20] = 5
In [93]: series.nunique()
Out[93]: 11
Суммирование данных: describe#
Существует удобный describe() функция, которая вычисляет различные сводные
статистики о Series или столбцах DataFrame (исключая NA,
конечно):
In [94]: series = pd.Series(np.random.randn(1000))
In [95]: series[::2] = np.nan
In [96]: series.describe()
Out[96]:
count 500.000000
mean -0.021292
std 1.015906
min -2.683763
25% -0.699070
50% -0.069718
75% 0.714483
max 3.160915
dtype: float64
In [97]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])
In [98]: frame.iloc[::2] = np.nan
In [99]: frame.describe()
Out[99]:
a b c d e
count 500.000000 500.000000 500.000000 500.000000 500.000000
mean 0.033387 0.030045 -0.043719 -0.051686 0.005979
std 1.017152 0.978743 1.025270 1.015988 1.006695
min -3.000951 -2.637901 -3.303099 -3.159200 -3.188821
25% -0.647623 -0.576449 -0.712369 -0.691338 -0.691115
50% 0.047578 -0.021499 -0.023888 -0.032652 -0.025363
75% 0.729907 0.775880 0.618896 0.670047 0.649748
max 2.740139 2.752332 3.004229 2.728702 3.240991
Вы можете выбрать конкретные процентили для включения в вывод:
In [100]: series.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
Out[100]:
count 500.000000
mean -0.021292
std 1.015906
min -2.683763
5% -1.645423
25% -0.699070
50% -0.069718
75% 0.714483
95% 1.711409
max 3.160915
dtype: float64
По умолчанию медиана всегда включается.
Для нечислового объекта Series, describe() даст простое
резюме количества уникальных значений и наиболее часто встречающихся значений:
In [101]: s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])
In [102]: s.describe()
Out[102]:
count 9
unique 4
top a
freq 5
dtype: object
Обратите внимание, что для объекта DataFrame смешанного типа, describe() ограничит сводку, включив только числовые столбцы или, если их нет, только
категориальные столбцы:
In [103]: frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})
In [104]: frame.describe()
Out[104]:
b
count 4.000000
mean 1.500000
std 1.290994
min 0.000000
25% 0.750000
50% 1.500000
75% 2.250000
max 3.000000
Это поведение можно контролировать, предоставляя список типов как include/exclude
аргументы. Специальное значение all также может использоваться:
In [105]: frame.describe(include=["object"])
Out[105]:
a
count 4
unique 2
top Yes
freq 2
In [106]: frame.describe(include=["number"])
Out[106]:
b
count 4.000000
mean 1.500000
std 1.290994
min 0.000000
25% 0.750000
50% 1.500000
75% 2.250000
max 3.000000
In [107]: frame.describe(include="all")
Out[107]:
a b
count 4 4.000000
unique 2 NaN
top Yes NaN
freq 2 NaN
mean NaN 1.500000
std NaN 1.290994
min NaN 0.000000
25% NaN 0.750000
50% NaN 1.500000
75% NaN 2.250000
max NaN 3.000000
Эта функция зависит от select_dtypes. См. там подробности о допустимых входных данных.
Индекс минимальных/максимальных значений#
The idxmin() и idxmax() функции на Series
и DataFrame вычисляют метки индекса с минимальными и максимальными
соответствующими значениями:
In [108]: s1 = pd.Series(np.random.randn(5))
In [109]: s1
Out[109]:
0 1.118076
1 -0.352051
2 -1.242883
3 -1.277155
4 -0.641184
dtype: float64
In [110]: s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)
In [111]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])
In [112]: df1
Out[112]:
A B C
0 -0.327863 -0.946180 -0.137570
1 -0.186235 -0.257213 -0.486567
2 -0.507027 -0.871259 -0.111110
3 2.000339 -2.430505 0.089759
4 -0.321434 -0.033695 0.096271
In [113]: df1.idxmin(axis=0)
Out[113]:
A 2
B 3
C 1
dtype: int64
In [114]: df1.idxmax(axis=1)
Out[114]:
0 C
1 A
2 C
3 A
4 C
dtype: object
Когда есть несколько строк (или столбцов), соответствующих минимальному или максимальному
значению, idxmin() и idxmax() вернуть первый
совпадающий индекс:
In [115]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))
In [116]: df3
Out[116]:
A
e 2.0
d 1.0
c 1.0
b 3.0
a NaN
In [117]: df3["A"].idxmin()
Out[117]: 'd'
Примечание
idxmin и idxmax называются argmin и argmax в NumPy.
Подсчёт значений (гистограмма) / мода#
The value_counts() Метод Series вычисляет гистограмму
одномерного массива значений. Он также может использоваться как функция для обычных массивов:
In [118]: data = np.random.randint(0, 7, size=50)
In [119]: data
Out[119]:
array([6, 6, 2, 3, 5, 3, 2, 5, 4, 5, 4, 3, 4, 5, 0, 2, 0, 4, 2, 0, 3, 2,
2, 5, 6, 5, 3, 4, 6, 4, 3, 5, 6, 4, 3, 6, 2, 6, 6, 2, 3, 4, 2, 1,
6, 2, 6, 1, 5, 4])
In [120]: s = pd.Series(data)
In [121]: s.value_counts()
Out[121]:
6 10
2 10
4 9
3 8
5 8
0 3
1 2
Name: count, dtype: int64
The value_counts() метод может использоваться для подсчета комбинаций по нескольким столбцам.
По умолчанию используются все столбцы, но подмножество можно выбрать с помощью subset аргумент.
In [122]: data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}
In [123]: frame = pd.DataFrame(data)
In [124]: frame.value_counts()
Out[124]:
a b
1 x 1
2 x 1
3 y 1
4 y 1
Name: count, dtype: int64
Аналогично, можно получить наиболее часто встречающееся значение(я), т.е. моду, значений в Series или DataFrame:
In [125]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])
In [126]: s5.mode()
Out[126]:
0 3
1 7
dtype: int64
In [127]: df5 = pd.DataFrame(
.....: {
.....: "A": np.random.randint(0, 7, size=50),
.....: "B": np.random.randint(-10, 15, size=50),
.....: }
.....: )
.....:
In [128]: df5.mode()
Out[128]:
A B
0 1.0 -9
1 NaN 10
2 NaN 13
Дискретизация и квантилирование#
Непрерывные значения могут быть дискретизированы с использованием cut() (корзины на основе значений)
и qcut() (бины на основе выборочных квантилей) функции:
In [129]: arr = np.random.randn(20)
In [130]: factor = pd.cut(arr, 4)
In [131]: factor
Out[131]:
[(-0.251, 0.464], (-0.968, -0.251], (0.464, 1.179], (-0.251, 0.464], (-0.968, -0.251], ..., (-0.251, 0.464], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251]]
Length: 20
Categories (4, interval[float64, right]): [(-0.968, -0.251] < (-0.251, 0.464] < (0.464, 1.179] <
(1.179, 1.893]]
In [132]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])
In [133]: factor
Out[133]:
[(0, 1], (-1, 0], (0, 1], (0, 1], (-1, 0], ..., (-1, 0], (-1, 0], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64, right]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]
qcut() вычисляет выборочные квантили. Например, мы можем разделить некоторые
нормально распределенные данные на равные квартили следующим образом:
In [134]: arr = np.random.randn(30)
In [135]: factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])
In [136]: factor
Out[136]:
[(0.569, 1.184], (-2.278, -0.301], (-2.278, -0.301], (0.569, 1.184], (0.569, 1.184], ..., (-0.301, 0.569], (1.184, 2.346], (1.184, 2.346], (-0.301, 0.569], (-2.278, -0.301]]
Length: 30
Categories (4, interval[float64, right]): [(-2.278, -0.301] < (-0.301, 0.569] < (0.569, 1.184] <
(1.184, 2.346]]
Мы также можем передавать бесконечные значения для определения интервалов:
In [137]: arr = np.random.randn(20)
In [138]: factor = pd.cut(arr, [-np.inf, 0, np.inf])
In [139]: factor
Out[139]:
[(-inf, 0.0], (0.0, inf], (0.0, inf], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (0.0, inf], (0.0, inf]]
Length: 20
Categories (2, interval[float64, right]): [(-inf, 0.0] < (0.0, inf]]
Применение функций#
Чтобы применить свои собственные функции или функции другой библиотеки к объектам pandas, вы должны знать о трех методах ниже. Подходящий метод для использования зависит от того, ожидает ли ваша функция работать со всем DataFrame или Series, построчно или постолбцово, или поэлементно.
Применение функции к таблице целиком#
DataFrames и Series можно передавать в функции.
Однако, если функцию нужно вызывать в цепочке, рассмотрите использование pipe() метод.
Сначала небольшая настройка:
In [140]: def extract_city_name(df):
.....: """
.....: Chicago, IL -> Chicago for city_name column
.....: """
.....: df["city_name"] = df["city_and_code"].str.split(",").str.get(0)
.....: return df
.....:
In [141]: def add_country_name(df, country_name=None):
.....: """
.....: Chicago -> Chicago-US for city_name column
.....: """
.....: col = "city_name"
.....: df["city_and_country"] = df[col] + country_name
.....: return df
.....:
In [142]: df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]})
extract_city_name и add_country_name являются функциями, принимающими и возвращающими DataFrames.
Теперь сравните следующее:
In [143]: add_country_name(extract_city_name(df_p), country_name="US")
Out[143]:
city_and_code city_name city_and_country
0 Chicago, IL Chicago ChicagoUS
Эквивалентно:
In [144]: df_p.pipe(extract_city_name).pipe(add_country_name, country_name="US")
Out[144]:
city_and_code city_name city_and_country
0 Chicago, IL Chicago ChicagoUS
pandas рекомендует второй стиль, известный как цепочка методов.
pipe позволяет легко использовать ваши собственные функции или функции другой библиотеки в цепочках методов вместе с методами pandas.
В примере выше функции extract_city_name и add_country_name каждый ожидал DataFrame в качестве первого позиционного аргумента.
Что делать, если функция, которую вы хотите применить, принимает свои данные как, скажем, второй аргумент?
В этом случае предоставьте pipe с кортежем из (callable, data_keyword).
.pipe направит DataFrame к аргументу, указанному в кортеже.
Например, мы можем подогнать регрессию с помощью statsmodels. Их API ожидает сначала формулу, а затем DataFrame в качестве второго аргумента, data. Мы передаем функцию и пару ключевых слов (sm.ols, 'data') to pipe:
In [147]: import statsmodels.formula.api as sm
In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")
In [149]: (
.....: bb.query("h > 0")
.....: .assign(ln_h=lambda df: np.log(df.h))
.....: .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
.....: .fit()
.....: .summary()
.....: )
.....:
Out[149]:
"""
OLS Regression Results
==============================================================================
Dep. Variable: hr R-squared: 0.685
Model: OLS Adj. R-squared: 0.665
Method: Least Squares F-statistic: 34.28
Date: Tue, 22 Nov 2022 Prob (F-statistic): 3.48e-15
Time: 05:34:17 Log-Likelihood: -205.92
No. Observations: 68 AIC: 421.8
Df Residuals: 63 BIC: 432.9
Df Model: 4
Covariance Type: nonrobust
===============================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------
Intercept -8484.7720 4664.146 -1.819 0.074 -1.78e+04 835.780
C(lg)[T.NL] -2.2736 1.325 -1.716 0.091 -4.922 0.375
ln_h -1.3542 0.875 -1.547 0.127 -3.103 0.395
year 4.2277 2.324 1.819 0.074 -0.417 8.872
g 0.1841 0.029 6.258 0.000 0.125 0.243
==============================================================================
Omnibus: 10.875 Durbin-Watson: 1.999
Prob(Omnibus): 0.004 Jarque-Bera (JB): 17.298
Skew: 0.537 Prob(JB): 0.000175
Kurtosis: 5.225 Cond. No. 1.49e+07
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
"""
Метод pipe вдохновлен unix-конвейерами и, более недавно, dplyr и magrittr, которые представили популярный (%>%) (оператор чтения pipe) для R.
Реализация pipe здесь довольно чистый и естественно вписывается в Python.
Мы рекомендуем вам просмотреть исходный код pipe().
Построчное или постолбцовое применение функции#
Произвольные функции могут применяться вдоль осей DataFrame
с использованием apply() метод, который, подобно описательным статистическим методам, принимает необязательный axis аргумент:
In [145]: df.apply(lambda x: np.mean(x))
Out[145]:
one 0.811094
two 1.360588
three 0.187958
dtype: float64
In [146]: df.apply(lambda x: np.mean(x), axis=1)
Out[146]:
a 1.583749
b 0.734929
c 1.133683
d -0.166914
dtype: float64
In [147]: df.apply(lambda x: x.max() - x.min())
Out[147]:
one 1.051928
two 1.632779
three 1.840607
dtype: float64
In [148]: df.apply(np.cumsum)
Out[148]:
one two three
a 1.394981 1.772517 NaN
b 1.738035 3.684640 -0.050390
c 2.433281 5.163008 1.177045
d NaN 5.442353 0.563873
In [149]: df.apply(np.exp)
Out[149]:
one two three
a 4.034899 5.885648 NaN
b 1.409244 6.767440 0.950858
c 2.004201 4.385785 3.412466
d NaN 1.322262 0.541630
The apply() метод также будет вызываться по имени строкового метода.
In [150]: df.apply("mean")
Out[150]:
one 0.811094
two 1.360588
three 0.187958
dtype: float64
In [151]: df.apply("mean", axis=1)
Out[151]:
a 1.583749
b 0.734929
c 1.133683
d -0.166914
dtype: float64
Тип возвращаемого значения функции, переданной в apply() влияет на
тип конечного вывода из DataFrame.apply для поведения по умолчанию:
Если применённая функция возвращает
Series, конечный вывод - этоDataFrame. Столбцы соответствуют индексуSeriesвозвращаемое применённой функцией.Если примененная функция возвращает любой другой тип, конечный вывод представляет собой
Series.
Это поведение по умолчанию можно переопределить с помощью result_type, который
принимает три опции: reduce, broadcast, и expand.
Это определит, как list-like значения возвращаются (или нет) в DataFrame.
apply() в сочетании с некоторой изобретательностью может использоваться для ответа на многие вопросы о наборе данных. Например, предположим, мы хотим извлечь дату, когда произошло максимальное значение для каждого столбца:
In [152]: tsdf = pd.DataFrame(
.....: np.random.randn(1000, 3),
.....: columns=["A", "B", "C"],
.....: index=pd.date_range("1/1/2000", periods=1000),
.....: )
.....:
In [153]: tsdf.apply(lambda x: x.idxmax())
Out[153]:
A 2000-08-06
B 2001-01-18
C 2001-07-18
dtype: datetime64[ns]
Вы также можете передать дополнительные аргументы и ключевые аргументы в apply()
метод.
In [154]: def subtract_and_divide(x, sub, divide=1):
.....: return (x - sub) / divide
.....:
In [155]: df_udf = pd.DataFrame(np.ones((2, 2)))
In [156]: df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]:
0 1
0 -1.333333 -1.333333
1 -1.333333 -1.333333
Еще одна полезная возможность — передача методов Series для выполнения некоторых операций Series над каждым столбцом или строкой:
In [157]: tsdf = pd.DataFrame(
.....: np.random.randn(10, 3),
.....: columns=["A", "B", "C"],
.....: index=pd.date_range("1/1/2000", periods=10),
.....: )
.....:
In [158]: tsdf.iloc[3:7] = np.nan
In [159]: tsdf
Out[159]:
A B C
2000-01-01 -0.158131 -0.232466 0.321604
2000-01-02 -1.810340 -3.105758 0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 -0.653602 0.178875 1.008298
2000-01-09 1.007996 0.462824 0.254472
2000-01-10 0.307473 0.600337 1.643950
In [160]: tsdf.apply(pd.Series.interpolate)
Out[160]:
A B C
2000-01-01 -0.158131 -0.232466 0.321604
2000-01-02 -1.810340 -3.105758 0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 -1.098598 -0.889659 0.092225
2000-01-05 -0.987349 -0.622526 0.321243
2000-01-06 -0.876100 -0.355392 0.550262
2000-01-07 -0.764851 -0.088259 0.779280
2000-01-08 -0.653602 0.178875 1.008298
2000-01-09 1.007996 0.462824 0.254472
2000-01-10 0.307473 0.600337 1.643950
Наконец, apply() принимает аргумент raw который по умолчанию имеет значение False, что
преобразует каждую строку или столбец в Series перед применением функции. Когда
установлено значение True, переданная функция будет получать объект ndarray, что
имеет положительные последствия для производительности, если вам не нужна функциональность
индексирования.
API агрегации#
API агрегации позволяет выразить несколько операций агрегации одним кратким способом.
Этот API схож для объектов pandas, см. API groupby,
window APIдолжны быть включены в старые категории. Значения, которые были в
удаленных категориях, будут установлены в NaN API resample.
Точка входа для агрегации — DataFrame.aggregate(), или псевдоним
DataFrame.agg().
Мы будем использовать аналогичный начальный фрейм сверху:
In [161]: tsdf = pd.DataFrame(
.....: np.random.randn(10, 3),
.....: columns=["A", "B", "C"],
.....: index=pd.date_range("1/1/2000", periods=10),
.....: )
.....:
In [162]: tsdf.iloc[3:7] = np.nan
In [163]: tsdf
Out[163]:
A B C
2000-01-01 1.257606 1.004194 0.167574
2000-01-02 -0.749892 0.288112 -0.757304
2000-01-03 -0.207550 -0.298599 0.116018
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.814347 -0.257623 0.869226
2000-01-09 -0.250663 -1.206601 0.896839
2000-01-10 2.169758 -1.333363 0.283157
Использование одной функции эквивалентно apply(). Вы также можете
передавать именованные методы как строки. Они вернут Series агрегированного вывода:
In [164]: tsdf.agg(lambda x: np.sum(x))
Out[164]:
A 3.033606
B -1.803879
C 1.575510
dtype: float64
In [165]: tsdf.agg("sum")
Out[165]:
A 3.033606
B -1.803879
C 1.575510
dtype: float64
# these are equivalent to a ``.sum()`` because we are aggregating
# on a single function
In [166]: tsdf.sum()
Out[166]:
A 3.033606
B -1.803879
C 1.575510
dtype: float64
Одиночные агрегации на Series это вернет скалярное значение:
In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146
Агрегирование с несколькими функциями#
Вы можете передать несколько аргументов агрегации в виде списка.
Результаты каждой из переданных функций будут строкой в результирующей DataFrame.
Они естественным образом названы по функции агрегации.
In [168]: tsdf.agg(["sum"])
Out[168]:
A B C
sum 3.033606 -1.803879 1.57551
Несколько функций дают несколько строк:
In [169]: tsdf.agg(["sum", "mean"])
Out[169]:
A B C
sum 3.033606 -1.803879 1.575510
mean 0.505601 -0.300647 0.262585
На Series, несколько функций возвращают Series, индексированные по именам функций:
In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]:
sum 3.033606
mean 0.505601
Name: A, dtype: float64
Передача lambda функция будет выдавать именованная строка:
In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]:
sum 3.033606
0.505601
Name: A, dtype: float64
Передача именованной функции даст это имя для строки:
In [172]: def mymean(x):
.....: return x.mean()
.....:
In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]:
sum 3.033606
mymean 0.505601
Name: A, dtype: float64
Агрегирование с помощью словаря#
Передача словаря имен столбцов в скаляр или список скаляров, чтобы DataFrame.agg
позволяет настроить, какие функции применяются к каким столбцам. Обратите внимание, что результаты не упорядочены, вы можете использовать OrderedDict вместо этого, чтобы гарантировать порядок.
In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]:
A 0.505601
B -1.803879
dtype: float64
Передача спискообразного объекта сгенерирует DataFrame вывод. Вы получите матричный вывод
всех агрегаторов. Вывод будет состоять из всех уникальных функций. Те, которые
не указаны для конкретного столбца, будут NaN:
In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]:
A B
mean 0.505601 NaN
min -0.749892 NaN
sum NaN -1.803879
Пользовательское описание#
С .agg() можно легко создать пользовательскую функцию describe, аналогичную встроенной функция describe.
In [176]: from functools import partial
In [177]: q_25 = partial(pd.Series.quantile, q=0.25)
In [178]: q_25.__name__ = "25%"
In [179]: q_75 = partial(pd.Series.quantile, q=0.75)
In [180]: q_75.__name__ = "75%"
In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]:
A B C
count 6.000000 6.000000 6.000000
mean 0.505601 -0.300647 0.262585
std 1.103362 0.887508 0.606860
min -0.749892 -1.333363 -0.757304
25% -0.239885 -0.979600 0.128907
median 0.303398 -0.278111 0.225365
75% 1.146791 0.151678 0.722709
max 2.169758 1.004194 0.896839
API преобразования#
The transform() метод возвращает объект, индексированный так же (того же размера),
как исходный. Этот API позволяет вам предоставить несколько операции одновременно, а не по одной. Его API довольно похож на .agg API.
Мы создаем фрейм, аналогичный тому, который использовался в предыдущих разделах.
In [182]: tsdf = pd.DataFrame(
.....: np.random.randn(10, 3),
.....: columns=["A", "B", "C"],
.....: index=pd.date_range("1/1/2000", periods=10),
.....: )
.....:
In [183]: tsdf.iloc[3:7] = np.nan
In [184]: tsdf
Out[184]:
A B C
2000-01-01 -0.428759 -0.864890 -0.675341
2000-01-02 -0.168731 1.338144 -1.279321
2000-01-03 -1.621034 0.438107 0.903794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 -1.240447 -0.201052
2000-01-09 -0.157795 0.791197 -1.144209
2000-01-10 -0.030876 0.371900 0.061932
Преобразовать весь фрейм. .transform() разрешает входные функции как: функция NumPy, строковое
имя функции или пользовательская функция.
In [185]: tsdf.transform(np.abs)
Out[185]:
A B C
2000-01-01 0.428759 0.864890 0.675341
2000-01-02 0.168731 1.338144 1.279321
2000-01-03 1.621034 0.438107 0.903794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 1.240447 0.201052
2000-01-09 0.157795 0.791197 1.144209
2000-01-10 0.030876 0.371900 0.061932
In [186]: tsdf.transform("abs")
Out[186]:
A B C
2000-01-01 0.428759 0.864890 0.675341
2000-01-02 0.168731 1.338144 1.279321
2000-01-03 1.621034 0.438107 0.903794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 1.240447 0.201052
2000-01-09 0.157795 0.791197 1.144209
2000-01-10 0.030876 0.371900 0.061932
In [187]: tsdf.transform(lambda x: x.abs())
Out[187]:
A B C
2000-01-01 0.428759 0.864890 0.675341
2000-01-02 0.168731 1.338144 1.279321
2000-01-03 1.621034 0.438107 0.903794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 1.240447 0.201052
2000-01-09 0.157795 0.791197 1.144209
2000-01-10 0.030876 0.371900 0.061932
Здесь transform() получена одна функция; это эквивалентно универсальная функция (ufunc) приложение.
In [188]: np.abs(tsdf)
Out[188]:
A B C
2000-01-01 0.428759 0.864890 0.675341
2000-01-02 0.168731 1.338144 1.279321
2000-01-03 1.621034 0.438107 0.903794
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 1.240447 0.201052
2000-01-09 0.157795 0.791197 1.144209
2000-01-10 0.030876 0.371900 0.061932
Передача одной функции в .transform() с Series даст единичный Series в ответе.
In [189]: tsdf["A"].transform(np.abs)
Out[189]:
2000-01-01 0.428759
2000-01-02 0.168731
2000-01-03 1.621034
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 NaN
2000-01-07 NaN
2000-01-08 0.254374
2000-01-09 0.157795
2000-01-10 0.030876
Freq: D, Name: A, dtype: float64
Преобразование с несколькими функциями#
Передача нескольких функций даст столбец DataFrame с MultiIndex. Первый уровень будет содержать исходные имена столбцов фрейма; второй уровень будет содержать имена преобразующих функций.
In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]:
A B C
absolute absolute absolute
2000-01-01 0.428759 0.571241 0.864890 0.135110 0.675341 0.324659
2000-01-02 0.168731 0.831269 1.338144 2.338144 1.279321 -0.279321
2000-01-03 1.621034 -0.621034 0.438107 1.438107 0.903794 1.903794
2000-01-04 NaN NaN NaN NaN NaN NaN
2000-01-05 NaN NaN NaN NaN NaN NaN
2000-01-06 NaN NaN NaN NaN NaN NaN
2000-01-07 NaN NaN NaN NaN NaN NaN
2000-01-08 0.254374 1.254374 1.240447 -0.240447 0.201052 0.798948
2000-01-09 0.157795 0.842205 0.791197 1.791197 1.144209 -0.144209
2000-01-10 0.030876 0.969124 0.371900 1.371900 0.061932 1.061932
Передача нескольких функций в Series даст DataFrame. Имена результирующих столбцов будут преобразующими функциями.
In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]:
absolute
2000-01-01 0.428759 0.571241
2000-01-02 0.168731 0.831269
2000-01-03 1.621034 -0.621034
2000-01-04 NaN NaN
2000-01-05 NaN NaN
2000-01-06 NaN NaN
2000-01-07 NaN NaN
2000-01-08 0.254374 1.254374
2000-01-09 0.157795 0.842205
2000-01-10 0.030876 0.969124
Преобразование с помощью словаря#
Передача словаря функций позволяет выборочно преобразовывать по столбцам.
In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]:
A B
2000-01-01 0.428759 0.135110
2000-01-02 0.168731 2.338144
2000-01-03 1.621034 1.438107
2000-01-04 NaN NaN
2000-01-05 NaN NaN
2000-01-06 NaN NaN
2000-01-07 NaN NaN
2000-01-08 0.254374 -0.240447
2000-01-09 0.157795 1.791197
2000-01-10 0.030876 1.371900
Передача словаря списков создаст DataFrame с MultiIndex с этими выборочными преобразованиями.
In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]:
A B
absolute sqrt
2000-01-01 0.428759 0.135110 NaN
2000-01-02 0.168731 2.338144 1.156782
2000-01-03 1.621034 1.438107 0.661897
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.254374 -0.240447 NaN
2000-01-09 0.157795 1.791197 0.889493
2000-01-10 0.030876 1.371900 0.609836
Применение поэлементных функций#
Поскольку не все функции могут быть векторизованы (принимать массивы NumPy и возвращать другой массив или значение), методы map() на DataFrame
и аналогично map() на Series принимают любую функцию Python, принимающую одно значение и возвращающую одно значение. Например:
In [194]: df4 = df.copy()
In [195]: df4
Out[195]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [196]: def f(x):
.....: return len(str(x))
.....:
In [197]: df4["one"].map(f)
Out[197]:
a 18
b 19
c 18
d 3
Name: one, dtype: int64
In [198]: df4.map(f)
Out[198]:
one two three
a 18 17 3
b 19 18 20
c 18 18 16
d 3 19 19
Series.map() имеет дополнительную функцию; его можно использовать для простого «связывания» или «отображения» значений, определенных вторичным рядом. Это тесно связано с функциональность слияния/объединения:
In [199]: s = pd.Series(
.....: ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
.....: )
.....:
In [200]: t = pd.Series({"six": 6.0, "seven": 7.0})
In [201]: s
Out[201]:
a six
b seven
c six
d seven
e six
dtype: object
In [202]: s.map(t)
Out[202]:
a 6.0
b 7.0
c 6.0
d 7.0
e 6.0
dtype: float64
Переиндексация и изменение меток#
reindex() является фундаментальным методом выравнивания данных в pandas.
Он используется для реализации почти всех других функций, полагающихся на функциональность
выравнивания по меткам. Чтобы reindex означает приведение данных в соответствие с заданным набором
меток вдоль определенной оси. Это достигает нескольких целей:
Переупорядочивает существующие данные в соответствии с новым набором меток
Вставляет маркеры пропущенных значений (NA) в позиции меток, где данных для этой метки не существовало
Если указано, fill данные для отсутствующих меток с использованием логики (очень актуально при работе с данными временных рядов)
Вот простой пример:
In [203]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [204]: s
Out[204]:
a 1.695148
b 1.328614
c 1.234686
d -0.385845
e -1.326508
dtype: float64
In [205]: s.reindex(["e", "b", "f", "d"])
Out[205]:
e -1.326508
b 1.328614
f NaN
d -0.385845
dtype: float64
Здесь f метка не содержалась в Series и поэтому отображается как
NaN в результате.
С DataFrame вы можете одновременно переиндексировать индекс и столбцы:
In [206]: df
Out[206]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [207]: df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
Out[207]:
three two one
c 1.227435 1.478369 0.695246
f NaN NaN NaN
b -0.050390 1.912123 0.343054
Обратите внимание, что Index объекты, содержащие фактические метки осей, могут быть
shared между объектами. Так, если у нас есть Series и DataFrame,
можно сделать следующее:
In [208]: rs = s.reindex(df.index)
In [209]: rs
Out[209]:
a 1.695148
b 1.328614
c 1.234686
d -0.385845
dtype: float64
In [210]: rs.index is df.index
Out[210]: True
Это означает, что индекс переиндексированного Series является тем же объектом Python, что и индекс DataFrame.
DataFrame.reindex() также поддерживает соглашение вызова в «стиле осей»,
где вы указываете один labels аргумент и axis к которому он применяется.
In [211]: df.reindex(["c", "f", "b"], axis="index")
Out[211]:
one two three
c 0.695246 1.478369 1.227435
f NaN NaN NaN
b 0.343054 1.912123 -0.050390
In [212]: df.reindex(["three", "two", "one"], axis="columns")
Out[212]:
three two one
a NaN 1.772517 1.394981
b -0.050390 1.912123 0.343054
c 1.227435 1.478369 0.695246
d -0.613172 0.279344 NaN
Смотрите также
MultiIndex / Расширенное индексирование является еще более кратким способом выполнения переиндексации.
Примечание
При написании кода, чувствительного к производительности, есть веская причина потратить некоторое время на освоение навыков переиндексации: многие операции выполняются быстрее на предварительно выровненных данных. Добавление двух невыровненных DataFrame внутренне запускает шаг переиндексации. Для исследовательского анализа вы вряд ли заметите разницу (потому что reindex был значительно оптимизирован), но когда важны циклы процессора, добавление нескольких явных reindex вызовы здесь и там могут иметь влияние.
Переиндексация для выравнивания с другим объектом#
Возможно, вы захотите взять объект и переиндексировать его оси, чтобы они были помечены так же, как
другой объект. Хотя синтаксис для этого прост, хотя и многословен, это
достаточно распространённая операция, что reindex_like() метод доступен для упрощения этого:
In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])
In [214]: df3 = df2 - df2.mean()
In [215]: df2
Out[215]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
In [216]: df3
Out[216]:
one two
a 0.583888 0.051514
b -0.468040 0.191120
c -0.115848 -0.242634
In [217]: df.reindex_like(df2)
Out[217]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
Выравнивание объектов друг с другом с align#
The align() метод — самый быстрый способ одновременного выравнивания двух объектов. Он
поддерживает join аргумент (связанный с объединение и слияние):
join='outer': взять объединение индексов (по умолчанию)
join='left': используйте индекс вызывающего объекта
join='right': использовать индекс переданного объекта
join='inner': пересечь индексы
Возвращает кортеж с обоими переиндексированными Series:
In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [219]: s1 = s[:4]
In [220]: s2 = s[1:]
In [221]: s1.align(s2)
Out[221]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
e NaN
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64)
In [222]: s1.align(s2, join="inner")
Out[222]:
(b -1.692424
c -0.303893
d -1.425662
dtype: float64,
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
In [223]: s1.align(s2, join="left")
Out[223]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
Для DataFrames метод join по умолчанию будет применён как к индексу, так и к столбцам:
In [224]: df.align(df2, join="inner")
Out[224]:
( one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
Вы также можете передать axis опция выравнивания только по указанной оси:
In [225]: df.align(df2, join="inner", axis=0)
Out[225]:
( one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
Если вы передаёте Series в DataFrame.align(), вы можете выбрать выравнивание обоих объектов либо по индексу, либо по столбцам DataFrame, используя axis аргумент:
In [226]: df.align(df2.iloc[0], axis=1)
Out[226]:
( one three two
a 1.394981 NaN 1.772517
b 0.343054 -0.050390 1.912123
c 0.695246 1.227435 1.478369
d NaN -0.613172 0.279344,
one 1.394981
three NaN
two 1.772517
Name: a, dtype: float64)
Заполнение при переиндексации#
reindex() принимает необязательный параметр method который является методом заполнения, выбранным из следующей таблицы:
Метод |
Действие |
|---|---|
pad / ffill |
Заполнение значений вперёд |
bfill / backfill |
Заполнить значения назад |
ближайший |
Заполнить из ближайшего значения индекса |
Мы иллюстрируем эти методы заполнения на простом Series:
In [227]: rng = pd.date_range("1/3/2000", periods=8)
In [228]: ts = pd.Series(np.random.randn(8), index=rng)
In [229]: ts2 = ts.iloc[[0, 3, 6]]
In [230]: ts
Out[230]:
2000-01-03 0.183051
2000-01-04 0.400528
2000-01-05 -0.015083
2000-01-06 2.395489
2000-01-07 1.414806
2000-01-08 0.118428
2000-01-09 0.733639
2000-01-10 -0.936077
Freq: D, dtype: float64
In [231]: ts2
Out[231]:
2000-01-03 0.183051
2000-01-06 2.395489
2000-01-09 0.733639
Freq: 3D, dtype: float64
In [232]: ts2.reindex(ts.index)
Out[232]:
2000-01-03 0.183051
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 NaN
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]:
2000-01-03 0.183051
2000-01-04 2.395489
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 0.733639
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
Эти методы требуют, чтобы индексы были ordered возрастающий или убывающий.
Обратите внимание, что тот же результат мог быть достигнут с помощью
ffill (кроме method='nearest') или
интерполировать:
In [236]: ts2.reindex(ts.index).ffill()
Out[236]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
reindex() вызовет ValueError, если индекс не монотонно возрастает или убывает. fillna() и interpolate()
не будет выполнять проверки порядка индекса.
Ограничения на заполнение при переиндексации#
The limit и tolerance аргументы предоставляют дополнительный контроль над
заполнением при переиндексации. Limit указывает максимальное количество последовательных
совпадений:
In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
В отличие от этого, допуск указывает максимальное расстояние между индексом и значениями индексатора:
In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
Обратите внимание, что при использовании на DatetimeIndex, TimedeltaIndex или
PeriodIndex, tolerance будет преобразовано в Timedelta если возможно.
Это позволяет указать допуск с помощью соответствующих строк.
Удаление меток с оси#
Метод, тесно связанный с reindex является drop() функция.
Она удаляет набор меток с оси:
In [239]: df
Out[239]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [240]: df.drop(["a", "d"], axis=0)
Out[240]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
In [241]: df.drop(["one"], axis=1)
Out[241]:
two three
a 1.772517 NaN
b 1.912123 -0.050390
c 1.478369 1.227435
d 0.279344 -0.613172
Обратите внимание, что следующее также работает, но менее очевидно / чисто:
In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
Переименование / сопоставление меток#
The rename() метод позволяет переименовать ось на основе некоторого отображения (словаря или Series) или произвольной функции.
In [243]: s
Out[243]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64
In [244]: s.rename(str.upper)
Out[244]:
A -0.186646
B -1.692424
C -0.303893
D -1.425662
E 1.114285
dtype: float64
Если вы передаете функцию, она должна возвращать значение при вызове с любой из меток (и должна создавать набор уникальных значений). Также можно использовать словарь или Series:
In [245]: df.rename(
.....: columns={"one": "foo", "two": "bar"},
.....: index={"a": "apple", "b": "banana", "d": "durian"},
.....: )
.....:
Out[245]:
foo bar three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
Если сопоставление не включает метку столбца/индекса, она не переименовывается. Обратите внимание, что дополнительные метки в сопоставлении не вызывают ошибку.
DataFrame.rename() также поддерживает соглашение вызова в стиле "оси", где вы указываете один mapper и axis для применения этого сопоставления.
In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]:
foo bar three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]:
one two three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
Наконец, rename() также принимает скалярное значение или список
для изменения Series.name атрибут.
In [248]: s.rename("scalar-name")
Out[248]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
Name: scalar-name, dtype: float64
Методы DataFrame.rename_axis() и Series.rename_axis()
разрешить определенные имена MultiIndex должны быть изменены (в отличие от
меток).
In [249]: df = pd.DataFrame(
.....: {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
.....: index=pd.MultiIndex.from_product(
.....: [["a", "b", "c"], [1, 2]], names=["let", "num"]
.....: ),
.....: )
.....:
In [250]: df
Out[250]:
x y
let num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [251]: df.rename_axis(index={"let": "abc"})
Out[251]:
x y
abc num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [252]: df.rename_axis(index=str.upper)
Out[252]:
x y
LET NUM
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
Итерация#
Поведение базовой итерации по объектам pandas зависит от типа. При итерации по Series он рассматривается как подобный массиву, и базовая итерация производит значения. DataFrames следуют соглашению, подобному словарю, итерируя по «ключам» объектов.
Короче говоря, базовая итерация (for i in object) выдаёт:
Series: значения
DataFrame: метки столбцов
Таким образом, например, итерация по DataFrame дает вам имена столбцов:
In [253]: df = pd.DataFrame(
.....: {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
.....: )
.....:
In [254]: for col in df:
.....: print(col)
.....:
col1
col2
Объекты pandas также имеют словарный интерфейс items() метод для
итерации по парам (ключ, значение).
Для итерации по строкам DataFrame можно использовать следующие методы:
iterrows(): Итерация по строкам DataFrame как парам (индекс, Series). Это преобразует строки в объекты Series, что может изменить типы данных и имеет некоторые последствия для производительности.itertuples(): Итерация по строкам DataFrame как namedtuples значений. Это намного быстрее, чемiterrows(), и в большинстве случаев предпочтительнее использовать для итерации по значениям DataFrame.
Предупреждение
Итерация по объектам pandas обычно медленно. Во многих случаях ручная итерация по строкам не требуется и может быть избегнута с помощью одного из следующих подходов:
Искать векторизованный решение: многие операции могут быть выполнены с использованием встроенных методов или функций NumPy, (булевой) индексации, …
Когда у вас есть функция, которая не может работать со всем DataFrame/Series сразу, лучше использовать
apply()вместо итерации по значениям. См. документацию по применение функции.Если вам нужно выполнять итеративные манипуляции со значениями, но важна производительность, рассмотрите написание внутреннего цикла с использованием cython или numba. См. улучшение производительности раздел для некоторых примеров этого подхода.
Предупреждение
Вам следует никогда не изменять что-то, по чему вы итерируете. Это не гарантирует работу во всех случаях. В зависимости от типов данных, итератор возвращает копию, а не представление, и запись в него не окажет эффекта!
Например, в следующем случае установка значения не имеет эффекта:
In [255]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})
In [256]: for index, row in df.iterrows():
.....: row["a"] = 10
.....:
In [257]: df
Out[257]:
a b
0 1 a
1 2 b
2 3 c
items#
В соответствии с интерфейсом, подобным словарю, items() перебирает
пары ключ-значение:
Series: пары (индекс, скалярное значение)
DataFrame: пары (столбец, Series)
Например:
In [258]: for label, ser in df.items():
.....: print(label)
.....: print(ser)
.....:
a
0 1
1 2
2 3
Name: a, dtype: int64
b
0 a
1 b
2 c
Name: b, dtype: object
iterrows#
iterrows() позволяет перебирать строки DataFrame как объекты Series. Возвращает итератор, выдающий каждое значение индекса вместе с Series, содержащим данные в каждой строке:
In [259]: for row_index, row in df.iterrows():
.....: print(row_index, row, sep="\n")
.....:
0
a 1
b a
Name: 0, dtype: object
1
a 2
b b
Name: 1, dtype: object
2
a 3
b c
Name: 2, dtype: object
Примечание
Потому что iterrows() возвращает Series для каждой строки,
он не сохранять типы данных по строкам (типы данных сохраняются по столбцам для DataFrame). Например,
In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])
In [261]: df_orig.dtypes
Out[261]:
int int64
float float64
dtype: object
In [262]: row = next(df_orig.iterrows())[1]
In [263]: row
Out[263]:
int 1.0
float 1.5
Name: 0, dtype: float64
Все значения в row, возвращаемый как Series, теперь преобразуется в числа с плавающей точкой, также исходное целочисленное значение в столбце x:
In [264]: row["int"].dtype
Out[264]: dtype('float64')
In [265]: df_orig["int"].dtype
Out[265]: dtype('int64')
Для сохранения типов данных при итерации по строкам лучше
использовать itertuples() который возвращает именованные кортежи значений и который обычно намного быстрее, чем iterrows().
Например, искусственный способ транспонирования DataFrame будет:
In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
In [267]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [268]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})
In [270]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
itertuples#
The itertuples() метод вернёт итератор,
выдающий namedtuple для каждой строки в DataFrame. Первый элемент
кортежа будет соответствующим значением индекса строки, а
остальные значения — значениями строки.
Например:
In [271]: for row in df.itertuples():
.....: print(row)
.....:
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')
Этот метод не преобразует строку в объект Series; он просто
возвращает значения внутри namedtuple. Поэтому,
itertuples() сохраняет тип данных значений
и обычно быстрее, так как iterrows().
Примечание
Имена столбцов будут переименованы в позиционные, если они являются недопустимыми идентификаторами Python, повторяются или начинаются с подчеркивания. При большом количестве столбцов (>255) возвращаются обычные кортежи.
.dt аксессор#
Series имеет аксессор для краткого возврата свойств, подобных datetime, для
values Series, если это Series типа datetime/period. Это вернет Series, проиндексированный как существующий Series.
# datetime
In [272]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))
In [273]: s
Out[273]:
0 2013-01-01 09:10:12
1 2013-01-02 09:10:12
2 2013-01-03 09:10:12
3 2013-01-04 09:10:12
dtype: datetime64[ns]
In [274]: s.dt.hour
Out[274]:
0 9
1 9
2 9
3 9
dtype: int32
In [275]: s.dt.second
Out[275]:
0 12
1 12
2 12
3 12
dtype: int32
In [276]: s.dt.day
Out[276]:
0 1
1 2
2 3
3 4
dtype: int32
Это позволяет использовать удобные выражения, такие как:
In [277]: s[s.dt.day == 2]
Out[277]:
1 2013-01-02 09:10:12
dtype: datetime64[ns]
Вы можете легко создавать преобразования с учетом часовых поясов:
In [278]: stz = s.dt.tz_localize("US/Eastern")
In [279]: stz
Out[279]:
0 2013-01-01 09:10:12-05:00
1 2013-01-02 09:10:12-05:00
2 2013-01-03 09:10:12-05:00
3 2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]
In [280]: stz.dt.tz
Out[280]:
Вы также можете объединять такие операции в цепочку:
In [281]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]:
0 2013-01-01 04:10:12-05:00
1 2013-01-02 04:10:12-05:00
2 2013-01-03 04:10:12-05:00
3 2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]
Вы также можете форматировать значения даты и времени как строки с помощью Series.dt.strftime() которая
поддерживает тот же формат, что и стандартный strftime().
# DatetimeIndex
In [282]: s = pd.Series(pd.date_range("20130101", periods=4))
In [283]: s
Out[283]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: datetime64[ns]
In [284]: s.dt.strftime("%Y/%m/%d")
Out[284]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
# PeriodIndex
In [285]: s = pd.Series(pd.period_range("20130101", periods=4))
In [286]: s
Out[286]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [287]: s.dt.strftime("%Y/%m/%d")
Out[287]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
The .dt аксессор работает для типов period и timedelta.
# period
In [288]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))
In [289]: s
Out[289]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [290]: s.dt.year
Out[290]:
0 2013
1 2013
2 2013
3 2013
dtype: int64
In [291]: s.dt.day
Out[291]:
0 1
1 2
2 3
3 4
dtype: int64
# timedelta
In [292]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))
In [293]: s
Out[293]:
0 1 days 00:00:05
1 1 days 00:00:06
2 1 days 00:00:07
3 1 days 00:00:08
dtype: timedelta64[ns]
In [294]: s.dt.days
Out[294]:
0 1
1 1
2 1
3 1
dtype: int64
In [295]: s.dt.seconds
Out[295]:
0 5
1 6
2 7
3 8
dtype: int32
In [296]: s.dt.components
Out[296]:
days hours minutes seconds milliseconds microseconds nanoseconds
0 1 0 0 5 0 0 0
1 1 0 0 6 0 0 0
2 1 0 0 7 0 0 0
3 1 0 0 8 0 0 0
Примечание
Series.dt вызовет TypeError если вы обращаетесь с не datetime-подобными значениями.
Векторизованные строковые методы#
Series оснащен набором методов обработки строк, которые упрощают работу с каждым элементом массива. Возможно, самое важное, что эти методы автоматически исключают пропущенные/NA значения. Доступ к ним осуществляется через
str атрибут и обычно имеют имена, соответствующие эквивалентным (скалярным) встроенным строковым методам. Например:
In [297]: s = pd.Series( .....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string" .....: ) .....: In [298]: s.str.lower() Out[298]: 0 a 1 b 2 c 3 aaba 4 baca 56 caba 7 dog 8 cat dtype: string
Также предоставлены мощные методы сопоставления с шаблонами, но обратите внимание, что сопоставление с шаблонами обычно использует регулярные выражения по умолчанию (и в некоторых случаях всегда использует их).
Примечание
До pandas 1.0 строковые методы были доступны только на object -тип данных
Series. pandas 1.0 добавил StringDtype который предназначен для строк. См. Текстовые типы данных подробнее.
Пожалуйста, смотрите Векторизованные строковые методы для полного описания.
Сортировка#
pandas поддерживает три вида сортировки: сортировку по меткам индекса, сортировку по значениям столбцов и сортировку по комбинации обоих.
По индексу#
The Series.sort_index() и DataFrame.sort_index() методы
используются для сортировки объекта pandas по его уровням индекса.
In [299]: df = pd.DataFrame(
.....: {
.....: "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
.....: "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
.....: "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
.....: }
.....: )
.....:
In [300]: unsorted_df = df.reindex(
.....: index=["a", "d", "c", "b"], columns=["three", "two", "one"]
.....: )
.....:
In [301]: unsorted_df
Out[301]:
three two one
a NaN -1.152244 0.562973
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]:
three two one
a NaN -1.152244 0.562973
b -0.098217 0.009797 -1.299504
c 1.273388 -0.167123 0.640382
d -0.252916 -0.109597 NaN
In [303]: unsorted_df.sort_index(ascending=False)
Out[303]:
three two one
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
a NaN -1.152244 0.562973
In [304]: unsorted_df.sort_index(axis=1)
Out[304]:
one three two
a 0.562973 NaN -1.152244
d NaN -0.252916 -0.109597
c 0.640382 1.273388 -0.167123
b -1.299504 -0.098217 0.009797
# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]:
a NaN
b -0.098217
c 1.273388
d -0.252916
Name: three, dtype: float64
Сортировка по индексу также поддерживает key параметром, который принимает вызываемую
функцию для применения к сортируемому индексу. Для MultiIndex объектах,
ключ применяется на уровне к уровням, указанным level.
In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
.....: list("ab")
.....: )
.....:
In [307]: s1
Out[307]:
c
a b
B 1 2
a 2 3
C 3 4
In [308]: s1.sort_index(level="a")
Out[308]:
c
a b
B 1 2
C 3 4
a 2 3
In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]:
c
a b
a 2 3
B 1 2
C 3 4
Для информации о сортировке ключей по значению см. сортировка значений.
По значениям#
The Series.sort_values() метод используется для сортировки Series по его значениям. The
DataFrame.sort_values() метод используется для сортировки DataFrame по его значениям столбца или строки.
Необязательный by параметр для DataFrame.sort_values() может использоваться для указания одного или нескольких столбцов
для определения порядка сортировки.
In [310]: df1 = pd.DataFrame(
.....: {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
.....: )
.....:
In [311]: df1.sort_values(by="two")
Out[311]:
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
The by параметр может принимать список имен столбцов, например:
In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]:
one two three
2 1 2 3
1 1 3 4
3 1 4 2
0 2 1 5
Эти методы имеют специальную обработку значений NA через na_position
аргумент:
In [313]: s[2] = np.nan
In [314]: s.sort_values()
Out[314]:
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
2
5
dtype: string
In [315]: s.sort_values(na_position="first")
Out[315]:
2
5
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
dtype: string
Сортировка также поддерживает key параметр, принимающий вызываемую функцию
для применения к сортируемым значениям.
In [316]: s1 = pd.Series(["B", "a", "C"])
In [317]: s1.sort_values()
Out[317]:
0 B
2 C
1 a
dtype: object
In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]:
1 a
0 B
2 C
dtype: object
key будет присвоено Series значений и должен возвращать Series
или массив той же формы с преобразованными значениями. Для DataFrame объекты,
ключ применяется к каждому столбцу, поэтому ключ по-прежнему должен ожидать Series и возвращать
Series, например.
In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]})
In [320]: df.sort_values(by="a")
Out[320]:
a b
0 B 1
2 C 3
1 a 2
In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]:
a b
1 a 2
0 B 1
2 C 3
Имя или тип каждого столбца может использоваться для применения различных функций к разным столбцам.
По индексам и значениям#
Строки, передаваемые как by параметр для DataFrame.sort_values() может
ссылаться либо на столбцы, либо на имена уровней индекса.
# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
.....: [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
.....: )
.....:
In [323]: idx.names = ["first", "second"]
# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)
In [325]: df_multi
Out[325]:
A
first second
a 1 6
2 5
2 4
b 2 3
1 2
1 1
Сортировка по 'second' (индекс) и 'A' (столбец)
In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]:
A
first second
b 1 1
1 2
a 1 6
b 2 3
a 2 4
2 5
Примечание
Если строка совпадает и с именем столбца, и с именем уровня индекса, то выдается предупреждение, и столбец имеет приоритет. Это приведет к ошибке неоднозначности в будущей версии.
searchsorted#
Series имеет searchsorted() метод, который работает аналогично
numpy.ndarray.searchsorted().
In [327]: ser = pd.Series([1, 2, 3])
In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])
In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])
In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])
In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])
In [332]: ser = pd.Series([3, 1, 2])
In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2])
наименьшие / наибольшие значения#
Series имеет nsmallest() и nlargest() методы, которые возвращают
наименьшее или наибольшее \(n\) значения. Для большого Series это может быть значительно
быстрее, чем сортировка всей Series и вызов head(n) на результат.
In [334]: s = pd.Series(np.random.permutation(10))
In [335]: s
Out[335]:
0 2
1 0
2 3
3 7
4 1
5 5
6 9
7 6
8 8
9 4
dtype: int64
In [336]: s.sort_values()
Out[336]:
1 0
4 1
0 2
2 3
9 4
5 5
7 6
3 7
8 8
6 9
dtype: int64
In [337]: s.nsmallest(3)
Out[337]:
1 0
4 1
0 2
dtype: int64
In [338]: s.nlargest(3)
Out[338]:
6 9
8 8
3 7
dtype: int64
DataFrame также имеет nlargest и nsmallest методы.
In [339]: df = pd.DataFrame(
.....: {
.....: "a": [-2, -1, 1, 10, 8, 11, -1],
.....: "b": list("abdceff"),
.....: "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
.....: }
.....: )
.....:
In [340]: df.nlargest(3, "a")
Out[340]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
In [341]: df.nlargest(5, ["a", "c"])
Out[341]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
2 1 d 4.0
6 -1 f 4.0
In [342]: df.nsmallest(3, "a")
Out[342]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
In [343]: df.nsmallest(5, ["a", "c"])
Out[343]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
2 1 d 4.0
4 8 e NaN
Сортировка по столбцу MultiIndex#
Вы должны явно указать сортировку, когда столбец является MultiIndex, и полностью указать
все уровни, чтобы by.
In [344]: df1.columns = pd.MultiIndex.from_tuples(
.....: [("a", "one"), ("a", "two"), ("b", "three")]
.....: )
.....:
In [345]: df1.sort_values(by=("a", "two"))
Out[345]:
a b
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
Копирование#
The copy() метод объектов pandas копирует базовые данные (но не индексы осей, так как они неизменяемы) и возвращает новый объект. Обратите внимание, что
редко возникает необходимость копировать объекты. Например, существует лишь
несколько способов изменить DataFrame на месте:
Вставка, удаление или изменение столбца.
Присваивание
indexилиcolumnsатрибуты.Для однородных данных прямое изменение значений через
valuesатрибут или расширенная индексация.
Чтобы было ясно, ни один метод pandas не имеет побочного эффекта изменения ваших данных; почти каждый метод возвращает новый объект, оставляя исходный объект нетронутым. Если данные изменены, это потому, что вы сделали это явно.
dtypes#
В основном pandas использует массивы NumPy и типы данных для Series или отдельных
столбцов DataFrame. NumPy предоставляет поддержку для float,
int, bool, timedelta64[ns] и datetime64[ns] (обратите внимание, что NumPy
не поддерживает даты и время с учетом часового пояса).
pandas и сторонние библиотеки расширить Система типов NumPy в нескольких местах. Этот раздел описывает расширения, которые pandas сделал внутренне. См. Расширенные типы о том, как написать собственное расширение, которое работает с pandas. См. страница экосистемы для списка сторонних библиотек, реализовавших расширение.
Следующая таблица перечисляет все типы расширений pandas. Для методов, требующих dtype
аргументы, строки могут быть указаны, как указано. См. соответствующие
разделы документации для получения дополнительной информации о каждом типе.
Тип данных |
Тип данных |
Скаляр |
Массив |
Строковые псевдонимы |
||
|---|---|---|---|---|---|---|
|
||||||
(нет) |
|
|||||
|
|
|||||
(нет) |
|
|||||
|
||||||
|
(нет) |
|
||||
|
(нет) |
|
||||
|
||||||
|
||||||
pandas имеет два способа хранения строк.
objectdtype, который может содержать любой объект Python, включая строки.StringDtype, который предназначен для строк.
Обычно мы рекомендуем использовать StringDtype. См. Текстовые типы данных подробнее.
Наконец, произвольные объекты могут храниться с использованием object dtype, но следует
избегать этого по возможности (для производительности и совместимости с
другими библиотеками и методами. См. преобразование объекта).
Удобный dtypes атрибут для DataFrame возвращает Series
с типом данных каждого столбца.
In [346]: dft = pd.DataFrame(
.....: {
.....: "A": np.random.rand(3),
.....: "B": 1,
.....: "C": "foo",
.....: "D": pd.Timestamp("20010102"),
.....: "E": pd.Series([1.0] * 3).astype("float32"),
.....: "F": False,
.....: "G": pd.Series([1] * 3, dtype="int8"),
.....: }
.....: )
.....:
In [347]: dft
Out[347]:
A B C D E F G
0 0.035962 1 foo 2001-01-02 1.0 False 1
1 0.701379 1 foo 2001-01-02 1.0 False 1
2 0.281885 1 foo 2001-01-02 1.0 False 1
In [348]: dft.dtypes
Out[348]:
A float64
B int64
C object
D datetime64[s]
E float32
F bool
G int8
dtype: object
На Series object, используйте dtype атрибут.
In [349]: dft["A"].dtype
Out[349]: dtype('float64')
Если объект pandas содержит данные с несколькими типами данных в одном столбце,
dtype столбца будет выбран так, чтобы вместить все типы данных
(object является наиболее общим).
# these ints are coerced to floats
In [350]: pd.Series([1, 2, 3, 4, 5, 6.0])
Out[350]:
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
# string data forces an ``object`` dtype
In [351]: pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]:
0 1
1 2
2 3
3 6.0
4 foo
dtype: object
Количество столбцов каждого типа в DataFrame можно найти, вызвав
DataFrame.dtypes.value_counts().
In [352]: dft.dtypes.value_counts()
Out[352]:
float64 1
int64 1
object 1
datetime64[s] 1
float32 1
bool 1
int8 1
Name: count, dtype: int64
Числовые dtypes будут распространяться и могут сосуществовать в DataFrames.
Если передан dtype (либо напрямую через dtype ключевое слово, переданное ndarray,
или переданный Series), тогда он будет сохранен в операциях DataFrame. Кроме того,
разные числовые типы данных будут НЕ можно комбинировать. Следующий пример даст вам представление.
In [353]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")
In [354]: df1
Out[354]:
A
0 0.224364
1 1.890546
2 0.182879
3 0.787847
4 -0.188449
5 0.667715
6 -0.011736
7 -0.399073
In [355]: df1.dtypes
Out[355]:
A float32
dtype: object
In [356]: df2 = pd.DataFrame(
.....: {
.....: "A": pd.Series(np.random.randn(8), dtype="float16"),
.....: "B": pd.Series(np.random.randn(8)),
.....: "C": pd.Series(np.random.randint(0, 255, size=8), dtype="uint8"), # [0,255] (range of uint8)
.....: }
.....: )
.....:
In [357]: df2
Out[357]:
A B C
0 0.823242 0.256090 26
1 1.607422 1.426469 86
2 -0.333740 -0.416203 46
3 -0.063477 1.139976 212
4 -1.014648 -1.193477 26
5 0.678711 0.096706 7
6 -0.040863 -1.956850 184
7 -0.357422 -0.714337 206
In [358]: df2.dtypes
Out[358]:
A float16
B float64
C uint8
dtype: object
значения по умолчанию#
По умолчанию целочисленные типы int64 и типы float являются float64,
независимо платформы (32-бит или 64-бит).
Все следующее приведет к int64 dtypes.
In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]:
a int64
dtype: object
In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]:
a int64
dtype: object
In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]:
a int64
dtype: object
Обратите внимание, что Numpy выберет зависит от платформы типов при создании массивов.
Следующие БУДЕТ приводит к int32 на 32-битной платформе.
In [362]: frame = pd.DataFrame(np.array([1, 2]))
повышение типа данных#
Типы могут быть повышенного типа при комбинации с другими типами, что означает их повышение
от текущего типа (например, int to float).
In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2
In [364]: df3
Out[364]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [365]: df3.dtypes
Out[365]:
A float32
B float64
C float64
dtype: object
DataFrame.to_numpy() вернет наименьший общий знаменатель из типов данных, означая
тип данных, который может вместить ВСЕ типов в результирующем однородном типизированном массиве NumPy. Это может
принудительно преобразовать некоторые повышение типа данных.
In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64')
astype#
Вы можете использовать astype() метод для явного преобразования типов данных из одного в другой. По умолчанию они возвращают копию,
даже если тип данных не изменился (передайте copy=False чтобы изменить это поведение). Кроме того, они вызовут
исключение, если операция astype недопустима.
Повышение типа всегда происходит в соответствии с NumPy правила. Если в операции участвуют два разных типа данных, то более общий будет использоваться как результат операции.
In [367]: df3
Out[367]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [368]: df3.dtypes
Out[368]:
A float32
B float64
C float64
dtype: object
# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]:
A float32
B float32
C float32
dtype: object
Преобразуйте подмножество столбцов к указанному типу с помощью astype().
In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)
In [372]: dft
Out[372]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [373]: dft.dtypes
Out[373]:
a uint8
b uint8
c int64
dtype: object
Преобразовать определенные столбцы в конкретный тип данных, передав словарь в astype().
In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})
In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})
In [376]: dft1
Out[376]:
a b c
0 True 4 7.0
1 False 5 8.0
2 True 6 9.0
In [377]: dft1.dtypes
Out[377]:
a bool
b int64
c float64
dtype: object
Примечание
При попытке преобразовать подмножество столбцов к указанному типу с использованием astype() и loc(), происходит повышение типа данных.
loc() пытается подогнать то, что мы присваиваем, к текущим типам данных, в то время как [] перезапишет их, принимая тип данных из правой части. Поэтому следующий фрагмент кода дает неожиданный результат.
In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]:
a uint8
b uint8
dtype: object
In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)
In [381]: dft.dtypes
Out[381]:
a int64
b int64
c int64
dtype: object
преобразование объекта#
pandas предлагает различные функции для попытки принудительного преобразования типов из object dtype в другие типы.
В случаях, когда данные уже имеют правильный тип, но хранятся в object массив,
DataFrame.infer_objects() и Series.infer_objects() методы могут использоваться для мягкого преобразования к правильному типу.
In [382]: import datetime In [383]: df = pd.DataFrame( .....: [ .....: [1, 2], .....: ["a", "b"], .....: [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)], .....: ] .....: ) .....: In [384]: df = df.T In [385]: df Out[385]: 0 1 2 0 1 a 2016-03-02 00:00:00 1 2 b 2016-03-02 00:00:00 In [386]: df.dtypes Out[386]: 0 object 1 object 2 object dtype: object
Поскольку данные были транспонированы, исходный вывод сохранил все столбцы как object, что
infer_objects исправит.
In [387]: df.infer_objects().dtypes Out[387]: 0 int64 1 object 2 datetime64[ns] dtype: object
Следующие функции доступны для одномерных массивов объектов или скаляров для выполнения жесткого преобразования объектов к указанному типу:
to_numeric()(преобразование в числовые типы данных)In [388]: m = ["1.1", 2, 3] In [389]: pd.to_numeric(m) Out[389]: array([1.1, 2. , 3. ])
to_datetime()(преобразование в объекты datetime)In [390]: import datetime In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)] In [392]: pd.to_datetime(m) Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
to_timedelta()(преобразование в объекты timedelta)In [393]: m = ["5us", pd.Timedelta("1day")] In [394]: pd.to_timedelta(m) Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
Для принудительного преобразования мы можем передать errors аргумент, который указывает, как pandas должен обрабатывать элементы,
которые не могут быть преобразованы в желаемый dtype или объект. По умолчанию, errors='raise', что означает, что любые ошибки,
встреченные в процессе, будут вызваны во время конвертации. Однако, если errors='coerce', эти ошибки будут проигнорированы, и pandas
преобразует проблемные элементы в pd.NaT (для datetime и timedelta) или np.nan (для числовых). Это может быть полезно, если вы читаете данные, которые в основном имеют желаемый тип данных (например, числовой, datetime), но иногда содержат несогласованные элементы, которые вы хотите представить как пропущенные:
In [395]: import datetime
In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]
In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [398]: m = ["apple", 2, 3]
In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan, 2., 3.])
In [400]: m = ["apple", pd.Timedelta("1day")]
In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
В дополнение к преобразованию объекта, to_numeric() предоставляет ещё один аргумент downcast, который предоставляет возможность понизить тип данных для новых (или уже существующих) числовых данных до меньшего dtype, что может сэкономить память:
In [402]: m = ["1", 2, 3]
In [403]: pd.to_numeric(m, downcast="integer") # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)
In [404]: pd.to_numeric(m, downcast="signed") # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)
In [405]: pd.to_numeric(m, downcast="unsigned") # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)
In [406]: pd.to_numeric(m, downcast="float") # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32)
Поскольку эти методы применяются только к одномерным массивам, спискам или скалярам; они не могут использоваться напрямую с многомерными объектами, такими как DataFrames. Однако с apply(), мы можем «применить» функцию к каждому столбцу эффективно:
In [407]: import datetime
In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")
In [409]: df
Out[409]:
0 1
0 2016-07-09 2016-03-02 00:00:00
1 2016-07-09 2016-03-02 00:00:00
In [410]: df.apply(pd.to_datetime)
Out[410]:
0 1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02
In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")
In [412]: df
Out[412]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [413]: df.apply(pd.to_numeric)
Out[413]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")
In [415]: df
Out[415]:
0 1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [416]: df.apply(pd.to_timedelta)
Out[416]:
0 1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days
подводные камни#
Выполнение операций выбора на integer тип данных может легко повысить тип данных до floating.
Тип данных входных данных будет сохранен в случаях, когда nans не вводятся.
См. также Поддержка целочисленных NA.
In [417]: dfi = df3.astype("int32")
In [418]: dfi["E"] = 1
In [419]: dfi
Out[419]:
A B C E
0 1 0 26 1
1 3 1 86 1
2 0 0 46 1
3 0 1 212 1
4 -1 -1 26 1
5 1 0 7 1
6 0 -1 184 1
7 0 0 206 1
In [420]: dfi.dtypes
Out[420]:
A int32
B int32
C int32
E int64
dtype: object
In [421]: casted = dfi[dfi > 0]
In [422]: casted
Out[422]:
A B C E
0 1.0 NaN 26 1
1 3.0 1.0 86 1
2 NaN NaN 46 1
3 NaN 1.0 212 1
4 NaN NaN 26 1
5 1.0 NaN 7 1
6 NaN NaN 184 1
7 NaN NaN 206 1
In [423]: casted.dtypes
Out[423]:
A float64
B float64
C int32
E int64
dtype: object
В то время как типы данных float остаются неизменными.
In [424]: dfa = df3.copy()
In [425]: dfa["A"] = dfa["A"].astype("float32")
In [426]: dfa.dtypes
Out[426]:
A float32
B float64
C float64
dtype: object
In [427]: casted = dfa[df2 > 0]
In [428]: casted
Out[428]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 NaN NaN 46.0
3 NaN 1.139976 212.0
4 NaN NaN 26.0
5 1.346426 0.096706 7.0
6 NaN NaN 184.0
7 NaN NaN 206.0
In [429]: casted.dtypes
Out[429]:
A float32
B float64
C float64
dtype: object
Выбор столбцов на основе dtype#
The select_dtypes() метод реализует выборку столбцов
на основе их dtype.
Сначала создадим DataFrame с множеством различных
tипов данных:
In [430]: df = pd.DataFrame(
.....: {
.....: "string": list("abc"),
.....: "int64": list(range(1, 4)),
.....: "uint8": np.arange(3, 6).astype("u1"),
.....: "float64": np.arange(4.0, 7.0),
.....: "bool1": [True, False, True],
.....: "bool2": [False, True, False],
.....: "dates": pd.date_range("now", periods=3),
.....: "category": pd.Series(list("ABC")).astype("category"),
.....: }
.....: )
.....:
In [431]: df["tdeltas"] = df.dates.diff()
In [432]: df["uint64"] = np.arange(3, 6).astype("u8")
In [433]: df["other_dates"] = pd.date_range("20130101", periods=3)
In [434]: df["tz_aware_dates"] = pd.date_range("20130101", periods=3, tz="US/Eastern")
In [435]: df
Out[435]:
string int64 uint8 ... uint64 other_dates tz_aware_dates
0 a 1 3 ... 3 2013-01-01 2013-01-01 00:00:00-05:00
1 b 2 4 ... 4 2013-01-02 2013-01-02 00:00:00-05:00
2 c 3 5 ... 5 2013-01-03 2013-01-03 00:00:00-05:00
[3 rows x 12 columns]
И типы данных:
In [436]: df.dtypes
Out[436]:
string object
int64 int64
uint8 uint8
float64 float64
bool1 bool
bool2 bool
dates datetime64[ns]
category category
tdeltas timedelta64[ns]
uint64 uint64
other_dates datetime64[ns]
tz_aware_dates datetime64[ns, US/Eastern]
dtype: object
select_dtypes() имеет два параметра include и exclude которые позволяют сказать «дайте мне колонки с эти типы данных" (include) и/или "указать
столбцы без эти типы данных" (exclude).
Например, чтобы выбрать bool столбцы:
In [437]: df.select_dtypes(include=[bool])
Out[437]:
bool1 bool2
0 True False
1 False True
2 True False
Вы также можете передать имя типа данных в Иерархия типов данных NumPy:
In [438]: df.select_dtypes(include=["bool"])
Out[438]:
bool1 bool2
0 True False
1 False True
2 True False
select_dtypes() также работает с общими типами данных.
Например, чтобы выбрать все числовые и булевы столбцы, исключая беззнаковые целые числа:
In [439]: df.select_dtypes(include=["number", "bool"], exclude=["unsignedinteger"])
Out[439]:
int64 float64 bool1 bool2 tdeltas
0 1 4.0 True False NaT
1 2 5.0 False True 1 days
2 3 6.0 True False 1 days
Для выбора строковых столбцов необходимо использовать object dtype:
In [440]: df.select_dtypes(include=["object"])
Out[440]:
string
0 a
1 b
2 c
Чтобы увидеть все дочерние типы данных общего dtype как numpy.number вы
можете определить функцию, которая возвращает дерево дочерних dtypes:
In [441]: def subdtypes(dtype):
.....: subs = dtype.__subclasses__()
.....: if not subs:
.....: return dtype
.....: return [dtype, [subdtypes(dt) for dt in subs]]
.....:
Все типы данных NumPy являются подклассами numpy.generic:
In [442]: subdtypes(np.generic)
Out[442]:
[numpy.generic,
[[numpy.number,
[[numpy.integer,
[[numpy.signedinteger,
[numpy.int8,
numpy.int16,
numpy.int32,
numpy.int64,
numpy.longlong,
numpy.timedelta64]],
[numpy.unsignedinteger,
[numpy.uint8,
numpy.uint16,
numpy.uint32,
numpy.uint64,
numpy.ulonglong]]]],
[numpy.inexact,
[[numpy.floating,
[numpy.float16, numpy.float32, numpy.float64, numpy.longdouble]],
[numpy.complexfloating,
[numpy.complex64, numpy.complex128, numpy.clongdouble]]]]]],
[numpy.flexible,
[[numpy.character, [numpy.bytes_, numpy.str_]],
[numpy.void, [numpy.record]]]],
numpy.bool_,
numpy.datetime64,
numpy.object_]]
Примечание
pandas также определяет типы category, и datetime64[ns, tz], которые не интегрированы в обычную
иерархию NumPy и не будут отображаться с помощью указанной выше функции.