每个单元格中对象的基础值是否有一个 pandas 访问器?
Is there a pandas accessor for whatever is the underlying value in the object in each cell?
在代码库中,我有 pandas 个包含自定义对象的对象 (pd.DataFrame
/ pd.Series
)。
如果我可以从底层对象调用方法或 属性 而无需求助于 .apply
.
,这将显着简化代码库
为了说明这一点,考虑一个 pandas 系列的“汽车”对象。
class Car:
...
def max_speed(self)->float:
...
x = pd.Series([car1, car2, car3])
目前我可以通过以下方式获得平均车速:
x.apply(lambda x: x.max_speed()).mean()
我认为如果我可以跳过 .apply(lambda x: x...)
并用类似的东西替换它会很好:
x.obj.max_speed().mean()
其中 obj
是我的自定义访问器。
为了进一步说明这一点,考虑 class Plane
class Plane:
def cruise_height(self)->float:
在我的代码库中:
x1 = pd.Series([car1, car2, car3])
x2 = pd.Series([plane1, plane2, plane3])
我可以用
得到平均车速/飞机巡航高度
x1.apply(lambda x: x.max_speed()).mean()
x2.apply(lambda x: x.cruise_height()).mean()
我认为如果可以的话,它的可读性会更好:
x1.obj.max_speed().mean()
x2.obj.cruise_height().mean()
我想这类似于 .str.
公开底层字符串方法的方式。
pd.Series(['Hello', 'World']).str.get(0) # returns ['H', 'W']
pd.Series(['Hello', 'World']).str.upper()
# etc
根据 Pandas 文档,您可以使用特殊装饰器注册 custom accessors,如下所示:
import pandas as pd
@pd.api.extensions.register_series_accessor("spec")
class SpecAccessor:
def __init__(self, pandas_obj: pd.Series):
self._obj = pandas_obj
for i in range(len(self._obj)):
for attr in self._obj[i].__class__.__dict__:
# set objects methods on the accessor
if not attr.startswith("__"):
ser = pd.Series(
[getattr(self._obj[i], attr)() for i in range(len(self._obj))]
)
setattr(self, attr, ser)
所以对于以下 类 和实例:
class Car:
def __init__(self, speed: float):
self._speed = speed
def max_speed(self) -> float:
return self._speed * 1.5
class Plane:
def __init__(self, max_height: float):
self._max_height = max_height
def cruise_height(self) -> float:
return self._max_height * 0.6
car1 = Car(10.0)
car2 = Car(30.5)
car3 = Car(50.9)
plane1 = Plane(5_000.0)
plane2 = Plane(3_000.5)
plane3 = Plane(9_000.9)
你可以这样做:
print(pd.Series([car1, car2, car3]).spec.max_speed)
# Ouputs
0 15.00
1 45.75
2 76.35
dtype: float64
print(pd.Series([plane1, plane2, plane3]).spec.cruise_height)
# Outputs
0 3000.00
1 1800.30
2 5400.54
dtype: float64
在代码库中,我有 pandas 个包含自定义对象的对象 (pd.DataFrame
/ pd.Series
)。
如果我可以从底层对象调用方法或 属性 而无需求助于 .apply
.
为了说明这一点,考虑一个 pandas 系列的“汽车”对象。
class Car:
...
def max_speed(self)->float:
...
x = pd.Series([car1, car2, car3])
目前我可以通过以下方式获得平均车速:
x.apply(lambda x: x.max_speed()).mean()
我认为如果我可以跳过 .apply(lambda x: x...)
并用类似的东西替换它会很好:
x.obj.max_speed().mean()
其中 obj
是我的自定义访问器。
为了进一步说明这一点,考虑 class Plane
class Plane:
def cruise_height(self)->float:
在我的代码库中:
x1 = pd.Series([car1, car2, car3])
x2 = pd.Series([plane1, plane2, plane3])
我可以用
得到平均车速/飞机巡航高度x1.apply(lambda x: x.max_speed()).mean()
x2.apply(lambda x: x.cruise_height()).mean()
我认为如果可以的话,它的可读性会更好:
x1.obj.max_speed().mean()
x2.obj.cruise_height().mean()
我想这类似于 .str.
公开底层字符串方法的方式。
pd.Series(['Hello', 'World']).str.get(0) # returns ['H', 'W']
pd.Series(['Hello', 'World']).str.upper()
# etc
根据 Pandas 文档,您可以使用特殊装饰器注册 custom accessors,如下所示:
import pandas as pd
@pd.api.extensions.register_series_accessor("spec")
class SpecAccessor:
def __init__(self, pandas_obj: pd.Series):
self._obj = pandas_obj
for i in range(len(self._obj)):
for attr in self._obj[i].__class__.__dict__:
# set objects methods on the accessor
if not attr.startswith("__"):
ser = pd.Series(
[getattr(self._obj[i], attr)() for i in range(len(self._obj))]
)
setattr(self, attr, ser)
所以对于以下 类 和实例:
class Car:
def __init__(self, speed: float):
self._speed = speed
def max_speed(self) -> float:
return self._speed * 1.5
class Plane:
def __init__(self, max_height: float):
self._max_height = max_height
def cruise_height(self) -> float:
return self._max_height * 0.6
car1 = Car(10.0)
car2 = Car(30.5)
car3 = Car(50.9)
plane1 = Plane(5_000.0)
plane2 = Plane(3_000.5)
plane3 = Plane(9_000.9)
你可以这样做:
print(pd.Series([car1, car2, car3]).spec.max_speed)
# Ouputs
0 15.00
1 45.75
2 76.35
dtype: float64
print(pd.Series([plane1, plane2, plane3]).spec.cruise_height)
# Outputs
0 3000.00
1 1800.30
2 5400.54
dtype: float64