Spec-Zone.ru › pandas 0.23

Расширение Pandas

Хотя pandas предоставляет богатый набор методов, контейнеров и типов данных, ваши потребности могут не быть полностью удовлетворены. Pandas предлагает несколько вариантов расширения pandas.

Регистрация пользовательских аксессоров

Библиотеки могут использовать декораторы pandas.api.extensions.register_dataframe_accessor(), pandas.api.extensions.register_series_accessor() и pandas.api.extensions.register_index_accessor(), чтобы добавить дополнительные «пространства имён» к объектам pandas. Все они следуют одному соглашению: вы декорируете класс, предоставляя имя добавляемого атрибута. Метод класса __init__ получает объект, который декорируется. Например:

@pd.api.extensions.register_dataframe_accessor("geo")
class GeoAccessor(object):
    def __init__(self, pandas_obj):
        self._obj = pandas_obj

    @property
    def center(self):
        # return the geographic center point of this DataFrame
        lat = self._obj.latitude
        lon = self._obj.longitude
        return (float(lon.mean()), float(lat.mean()))

    def plot(self):
        # plot this array's data on a map, e.g., using Cartopy
        pass

Теперь пользователи могут получить доступ к вашим методам, используя пространство имён geo:

>>> ds = pd.DataFrame({'longitude': np.linspace(0, 10),
...                    'latitude': np.linspace(0, 20)})
>>> ds.geo.center
(5.0, 10.0)
>>> ds.geo.plot()
# plots data on a map

Это может быть удобным способом расширения объектов pandas без их подклассирования. Если вы напишете пользовательский аксессор, отправьте запрос на добавление его на страницу нашего экосистемы pandas.

Типы расширения

Новое в версии 0.23.0.

Предупреждение

API pandas.api.extension.ExtensionDtype и pandas.api.extension.ExtensionArray новые и экспериментальные. Они могут изменяться между версиями без предупреждения.

Pandas определяет интерфейс для реализации типов данных и массивов, которые расширяют систему типов NumPy. Сам Pandas использует систему расширений для некоторых типов, которые не встроены в NumPy (категориальные, периодические, интервальные, datetime с часовым поясом).

Библиотеки могут определять пользовательский массив и тип данных. Когда pandas сталкивается с этими объектами, они обрабатываются должным образом (т. е. не преобразуются в массив ndarray объектов). Многие методы, такие как pandas.isna(), будут обращаться к реализации типа расширения.

Если вы разрабатываете библиотеку, которая реализует интерфейс, пожалуйста, опубликуйте её на странице типов расширенных данных.

Интерфейс состоит из двух классов.

ExtensionDtype

Тип pandas.api.extension.ExtensionDtype похож на объект numpy.dtype. Он описывает тип данных. Реализаторы несут ответственность за несколько уникальных элементов, таких как имя.

Одним из особенно важных элементов является свойство type. Это должен быть класс, который является скалярным типом для ваших данных. Например, если вы писали расширенный массив для данных IP-адреса, это может быть ipaddress.IPv4Address.

Обратитесь к исходному коду типа расширенного типа для определения интерфейса.

ExtensionArray

Этот класс предоставляет все функциональные возможности массивоподобных объектов. ExtensionArrays ограничены 1 измерением. ExtensionArray связан с ExtensionDtype через атрибут dtype.

Pandas не накладывает никаких ограничений на то, как создаётся массив расширения с помощью __new__ или __init__, и не накладывает никаких ограничений на то, как вы храните свои данные. Мы требуем, чтобы ваш массив можно было преобразовать в массив NumPy, даже если это относительно дорого (как в случае с Categorical).

Они могут быть основаны на нуле, одном или нескольких массивах NumPy. Например, pandas.Categorical — это расширенный массив, основанный на двух массивах, одном для кодов и одном для категорий. Массив IPv6-адресов может быть основан на структурированном массиве NumPy с двумя полями, одним для нижних 64 бит и одним для верхних 64 бит. Или они могут быть основаны на другом типе хранения, таком как списки Python.

Обратитесь к исходному коду расширенного массива для определения интерфейса. В строках документации и комментариях содержатся рекомендации по правильному внедрению интерфейса.

Мы предоставляем набор тестов, чтобы убедиться, что ваши расширенные массивы соответствуют ожидаемому поведению. Чтобы использовать набор тестов, вы должны предоставить несколько фикстур pytest и унаследовать от базового тестового класса. Необходимые фикстуры можно найти в https://github.com/pandas-dev/pandas/blob/master/pandas/tests/extension/conftest.py.

Чтобы использовать тест, подклассифицируйте его:

from pandas.tests.extension import base

class TestConstructors(base.BaseConstructorsTests):
    pass

См. https://github.com/pandas-dev/pandas/blob/master/pandas/tests/extension/base/__init__.py для списка всех доступных тестов.

Подклассирование структур данных pandas

Предупреждение

Есть несколько более простых альтернатив, прежде чем рассматривать подклассирование pandas структур данных.

  1. Расширяемые цепочки методов с помощью pipe
  2. Используйте композицию. См. здесь.
  3. Расширение путём регистрации аксессора
  4. Расширение путём типа расширения

Этот раздел описывает, как подклассировать pandas структуры данных для удовлетворения более специфических потребностей. Есть два момента, которые требуют внимания:

  1. Переопределить свойства конструктора.
  2. Определить исходные свойства

Примечание

Вы можете найти хороший пример в проекте geopandas.

Переопределение свойств конструктора

Каждая структура данных имеет несколько свойств конструктора для возвращения новой структуры данных в результате операции. Переопределяя эти свойства, вы можете сохранить подклассы при манипулировании данными pandas.

Существует 3 свойства конструктора, которые необходимо определить:

  • _constructor: Используется, когда результат манипуляции имеет такие же размеры, как и оригинал.
  • _constructor_sliced: Используется, когда результат манипуляции имеет на одну размерность (размеры) меньше, чем оригинал, например, при выделении одной колонки.
  • _constructor_expanddim: Используется, когда результат манипуляции имеет на одну размерность больше, чем оригинал, например, при Series.to_frame() и DataFrame.to_panel().

В следующей таблице показано, как pandas структуры данных определяют свойства конструктора по умолчанию.

Атрибуты свойства Series DataFrame
_constructor Series DataFrame
_constructor_sliced NotImplementedError Series
_constructor_expanddim DataFrame Panel

Ниже приведен пример того, как определить SubclassedSeries и SubclassedDataFrame, переопределяя свойства конструктора.

class SubclassedSeries(Series):

    @property
    def _constructor(self):
        return SubclassedSeries

    @property
    def _constructor_expanddim(self):
        return SubclassedDataFrame

class SubclassedDataFrame(DataFrame):

    @property
    def _constructor(self):
        return SubclassedDataFrame

    @property
    def _constructor_sliced(self):
        return SubclassedSeries
>>> s = SubclassedSeries([1, 2, 3])
>>> type(s)
<class '__main__.SubclassedSeries'>

>>> to_framed = s.to_frame()
>>> type(to_framed)
<class '__main__.SubclassedDataFrame'>

>>> df = SubclassedDataFrame({'A', [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
>>> df
   A  B  C
0  1  4  7
1  2  5  8
2  3  6  9

>>> type(df)
<class '__main__.SubclassedDataFrame'>

>>> sliced1 = df[['A', 'B']]
>>> sliced1
   A  B
0  1  4
1  2  5
2  3  6
>>> type(sliced1)
<class '__main__.SubclassedDataFrame'>

>>> sliced2 = df['A']
>>> sliced2
0    1
1    2
2    3
Name: A, dtype: int64
>>> type(sliced2)
<class '__main__.SubclassedSeries'>

Определение исходных свойств

Чтобы позволить исходным структурам данных иметь дополнительные свойства, вы должны сообщить pandas о добавленных свойствах. pandas сопоставляет неизвестные свойства с именами данных, переопределяя __getattribute__. Определение исходных свойств можно выполнить двумя способами:

  1. Определите _internal_names и _internal_names_set для временных свойств, которые НЕ будут переданы в результаты манипуляций.
  2. Определите _metadata для обычных свойств, которые будут переданы в результаты манипуляций.

Ниже приведен пример определения двух исходных свойств: «internal_cache» как временного свойства и «added_property» как обычного свойства

class SubclassedDataFrame2(DataFrame):

    # temporary properties
    _internal_names = pd.DataFrame._internal_names + ['internal_cache']
    _internal_names_set = set(_internal_names)

    # normal properties
    _metadata = ['added_property']

    @property
    def _constructor(self):
        return SubclassedDataFrame2
>>> df = SubclassedDataFrame2({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
>>> df
   A  B  C
0  1  4  7
1  2  5  8
2  3  6  9

>>> df.internal_cache = 'cached'
>>> df.added_property = 'property'

>>> df.internal_cache
cached
>>> df.added_property
property

# properties defined in _internal_names is reset after manipulation
>>> df[['A', 'B']].internal_cache
AttributeError: 'SubclassedDataFrame2' object has no attribute 'internal_cache'

# properties defined in _metadata are retained
>>> df[['A', 'B']].added_property
property

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.23.4/extending.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API