Internals

本节将介绍一些大熊猫内部。

Indexing

在大熊猫中有一些实现的对象可以作为轴标签的有效容器:

  • Index:通用的“有序集合”对象,对象类型的ndarray,不考虑其内容。标签必须是可哈希的(并且可能是不可变的)和唯一的。在Cython中填充标签的位置以执行O(1)查找。
  • Int64Index:针对64位整数数据(例如时间戳)高度优化的Index版本
  • Float64Index:针对64位浮点数据进行高度优化的Index版本
  • MultiIndex:标准层次索引对象
  • DatetimeIndex:带有Timestamp框元素的索引对象(impl是int64值)
  • TimedeltaIndex:具有Timedelta框元素的索引对象(impl是in64值)
  • PeriodIndex:包含Period元素的索引对象

有一些功能使得创建常规索引变得容易:

  • date_range:从时间规则或DateOffset生成的固定频率日期范围。Python datetime对象的ndarray
  • period_range:从时间规则或DateOffset生成的固定频率日期范围。Period对象的数组,表示时间段

首先具有Index类的动机是启用不同的索引实现。这意味着,用户可以实现一个自定义Index子类,它可能更适合于特定应用程序,而不是在pandas中提供的。

从内部实现的角度来看,Index必须定义的相关方法是以下一个或多个(取决于新对象内部与Index功能):

  • get_loc:为标签返回“indexer”(整数,或在某些情况下为slice对象)
  • slice_locs:返回“范围”到两个标签之间的切片
  • get_indexer:计算用于重建索引/数据对齐目的的索引向量。有关更多信息,请参阅source / docstrings
  • get_indexer_non_unique:当索引非唯一时,计算索引向量以进行重建索引/数据对齐。有关更多信息,请参阅source / docstrings
  • reindex:输入索引的任何预转换然后调用get_indexer
  • unionintersection:计算两个Index对象的并集或交集
  • insert:在索引中插入一个新标签,产生一个新对象
  • delete:删除标签,生成一个新对象
  • drop:删除一组标签
  • take:类似于ndarray.take

MultiIndex

在内部,MultiIndex包含以下几项内容:级别,整数标签和级别名称

In [1]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])

In [2]: index
Out[2]: 
MultiIndex(levels=[[0, 1, 2], [u'one', u'two']],
           labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
           names=[u'first', u'second'])

In [3]: index.levels
Out[3]: FrozenList([[0, 1, 2], [u'one', u'two']])

In [4]: index.labels
Out[4]: FrozenList([[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])

In [5]: index.names
Out[5]: FrozenList([u'first', u'second'])

您可能猜测标签确定在索引的每个层上用该位置标识哪个唯一元素。It’s important to note that sortedness is determined solely from the integer labels and does not check (or care) whether the levels themselves are sorted. 幸运的是,构造函数from_tuplesfrom_arrays确保这是真的,但如果你自己计算级别和标签,请小心。

Subclassing pandas Data Structures

警告

在考虑子类化pandas数据结构之前,有一些更简单的替代方法。

  1. 具有pipe
  2. 使用组合请参阅此处

本节介绍如何子类化pandas数据结构以满足更具体的需求。有2分需要注意:

  1. 覆盖构造函数属性。
  2. 定义原始属性

注意

你可以在geopandas项目中找到一个很好的例子。

Override Constructor Properties

每个数据结构都具有指定数据构造函数的构造函数属性。通过覆盖这些属性,您可以通过pandas数据操作来保留定义类。

有3个构造函数要定义:

  • _constructor:当操作结果与原始操作结果具有相同的缩放时使用。
  • _constructor_sliced:当操作结果具有一个较低维度作为原始值时使用,例如DataFrame单列切片。
  • _constructor_expanddim:当操作结果具有一个较高维度作为原始值时使用,例如Series.to_frame()DataFrame.to_panel()

下表显示了默认情况下pandas数据结构如何定义构造函数属性。

属性属性 Series DataFrame Panel
_constructor Series DataFrame Panel
_constructor_sliced NotImplementedError Series DataFrame
_constructor_expanddim DataFrame Panel NotImplementedError

下面的示例显示如何定义SubclassedSeriesSubclassedDataFrame覆盖构造函数属性。

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'>

Define Original Properties

要让原始数据结构具有其他属性,您应该让pandas知道添加了什么属性。pandas将未知属性映射到覆盖__getattribute__的数据名称。定义原始属性可以通过以下两种方式之一完成:

  1. 为不会传递到操作结果的临时属性定义_internal_names_internal_names_set
  2. 为将传递到操作结果的正常属性定义_metadata

下面是一个定义2个原始属性的示例,“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
Scroll To Top