Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/accessor.py: 64%

80 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2 

3accessor.py contains base classes for implementing accessor properties 

4that can be mixed into or pinned onto other pandas classes. 

5 

6""" 

7from __future__ import annotations 

8 

9import warnings 

10 

11from pandas.util._decorators import doc 

12from pandas.util._exceptions import find_stack_level 

13 

14 

15class DirNamesMixin: 

16 _accessors: set[str] = set() 

17 _hidden_attrs: frozenset[str] = frozenset() 

18 

19 def _dir_deletions(self) -> set[str]: 

20 """ 

21 Delete unwanted __dir__ for this object. 

22 """ 

23 return self._accessors | self._hidden_attrs 

24 

25 def _dir_additions(self) -> set[str]: 

26 """ 

27 Add additional __dir__ for this object. 

28 """ 

29 return {accessor for accessor in self._accessors if hasattr(self, accessor)} 

30 

31 def __dir__(self) -> list[str]: 

32 """ 

33 Provide method name lookup and completion. 

34 

35 Notes 

36 ----- 

37 Only provide 'public' methods. 

38 """ 

39 rv = set(super().__dir__()) 

40 rv = (rv - self._dir_deletions()) | self._dir_additions() 

41 return sorted(rv) 

42 

43 

44class PandasDelegate: 

45 """ 

46 Abstract base class for delegating methods/properties. 

47 """ 

48 

49 def _delegate_property_get(self, name, *args, **kwargs): 

50 raise TypeError(f"You cannot access the property {name}") 

51 

52 def _delegate_property_set(self, name, value, *args, **kwargs): 

53 raise TypeError(f"The property {name} cannot be set") 

54 

55 def _delegate_method(self, name, *args, **kwargs): 

56 raise TypeError(f"You cannot call method {name}") 

57 

58 @classmethod 

59 def _add_delegate_accessors( 

60 cls, delegate, accessors, typ: str, overwrite: bool = False 

61 ): 

62 """ 

63 Add accessors to cls from the delegate class. 

64 

65 Parameters 

66 ---------- 

67 cls 

68 Class to add the methods/properties to. 

69 delegate 

70 Class to get methods/properties and doc-strings. 

71 accessors : list of str 

72 List of accessors to add. 

73 typ : {'property', 'method'} 

74 overwrite : bool, default False 

75 Overwrite the method/property in the target class if it exists. 

76 """ 

77 

78 def _create_delegator_property(name): 

79 def _getter(self): 

80 return self._delegate_property_get(name) 

81 

82 def _setter(self, new_values): 

83 return self._delegate_property_set(name, new_values) 

84 

85 _getter.__name__ = name 

86 _setter.__name__ = name 

87 

88 return property( 

89 fget=_getter, fset=_setter, doc=getattr(delegate, name).__doc__ 

90 ) 

91 

92 def _create_delegator_method(name): 

93 def f(self, *args, **kwargs): 

94 return self._delegate_method(name, *args, **kwargs) 

95 

96 f.__name__ = name 

97 f.__doc__ = getattr(delegate, name).__doc__ 

98 

99 return f 

100 

101 for name in accessors: 

102 

103 if typ == "property": 

104 f = _create_delegator_property(name) 

105 else: 

106 f = _create_delegator_method(name) 

107 

108 # don't overwrite existing methods/properties 

109 if overwrite or not hasattr(cls, name): 

110 setattr(cls, name, f) 

111 

112 

113def delegate_names(delegate, accessors, typ: str, overwrite: bool = False): 

114 """ 

115 Add delegated names to a class using a class decorator. This provides 

116 an alternative usage to directly calling `_add_delegate_accessors` 

117 below a class definition. 

118 

119 Parameters 

120 ---------- 

121 delegate : object 

122 The class to get methods/properties & doc-strings. 

123 accessors : Sequence[str] 

124 List of accessor to add. 

125 typ : {'property', 'method'} 

126 overwrite : bool, default False 

127 Overwrite the method/property in the target class if it exists. 

128 

129 Returns 

130 ------- 

131 callable 

132 A class decorator. 

133 

134 Examples 

135 -------- 

136 @delegate_names(Categorical, ["categories", "ordered"], "property") 

137 class CategoricalAccessor(PandasDelegate): 

138 [...] 

139 """ 

140 

141 def add_delegate_accessors(cls): 

142 cls._add_delegate_accessors(delegate, accessors, typ, overwrite=overwrite) 

143 return cls 

144 

145 return add_delegate_accessors 

146 

147 

148# Ported with modifications from xarray 

149# https://github.com/pydata/xarray/blob/master/xarray/core/extensions.py 

150# 1. We don't need to catch and re-raise AttributeErrors as RuntimeErrors 

151# 2. We use a UserWarning instead of a custom Warning 

152 

153 

154class CachedAccessor: 

155 """ 

156 Custom property-like object. 

157 

158 A descriptor for caching accessors. 

159 

160 Parameters 

161 ---------- 

162 name : str 

163 Namespace that will be accessed under, e.g. ``df.foo``. 

164 accessor : cls 

165 Class with the extension methods. 

166 

167 Notes 

168 ----- 

169 For accessor, The class's __init__ method assumes that one of 

170 ``Series``, ``DataFrame`` or ``Index`` as the 

171 single argument ``data``. 

172 """ 

173 

174 def __init__(self, name: str, accessor) -> None: 

175 self._name = name 

176 self._accessor = accessor 

177 

178 def __get__(self, obj, cls): 

179 if obj is None: 

180 # we're accessing the attribute of the class, i.e., Dataset.geo 

181 return self._accessor 

182 accessor_obj = self._accessor(obj) 

183 # Replace the property with the accessor object. Inspired by: 

184 # https://www.pydanny.com/cached-property.html 

185 # We need to use object.__setattr__ because we overwrite __setattr__ on 

186 # NDFrame 

187 object.__setattr__(obj, self._name, accessor_obj) 

188 return accessor_obj 

189 

190 

191@doc(klass="", others="") 

192def _register_accessor(name, cls): 

193 """ 

194 Register a custom accessor on {klass} objects. 

195 

196 Parameters 

197 ---------- 

198 name : str 

199 Name under which the accessor should be registered. A warning is issued 

200 if this name conflicts with a preexisting attribute. 

201 

202 Returns 

203 ------- 

204 callable 

205 A class decorator. 

206 

207 See Also 

208 -------- 

209 register_dataframe_accessor : Register a custom accessor on DataFrame objects. 

210 register_series_accessor : Register a custom accessor on Series objects. 

211 register_index_accessor : Register a custom accessor on Index objects. 

212 

213 Notes 

214 ----- 

215 When accessed, your accessor will be initialized with the pandas object 

216 the user is interacting with. So the signature must be 

217 

218 .. code-block:: python 

219 

220 def __init__(self, pandas_object): # noqa: E999 

221 ... 

222 

223 For consistency with pandas methods, you should raise an ``AttributeError`` 

224 if the data passed to your accessor has an incorrect dtype. 

225 

226 >>> pd.Series(['a', 'b']).dt 

227 Traceback (most recent call last): 

228 ... 

229 AttributeError: Can only use .dt accessor with datetimelike values 

230 

231 Examples 

232 -------- 

233 In your library code:: 

234 

235 import pandas as pd 

236 

237 @pd.api.extensions.register_dataframe_accessor("geo") 

238 class GeoAccessor: 

239 def __init__(self, pandas_obj): 

240 self._obj = pandas_obj 

241 

242 @property 

243 def center(self): 

244 # return the geographic center point of this DataFrame 

245 lat = self._obj.latitude 

246 lon = self._obj.longitude 

247 return (float(lon.mean()), float(lat.mean())) 

248 

249 def plot(self): 

250 # plot this array's data on a map, e.g., using Cartopy 

251 pass 

252 

253 Back in an interactive IPython session: 

254 

255 .. code-block:: ipython 

256 

257 In [1]: ds = pd.DataFrame({{"longitude": np.linspace(0, 10), 

258 ...: "latitude": np.linspace(0, 20)}}) 

259 In [2]: ds.geo.center 

260 Out[2]: (5.0, 10.0) 

261 In [3]: ds.geo.plot() # plots data on a map 

262 """ 

263 

264 def decorator(accessor): 

265 if hasattr(cls, name): 

266 warnings.warn( 

267 f"registration of accessor {repr(accessor)} under name " 

268 f"{repr(name)} for type {repr(cls)} is overriding a preexisting " 

269 f"attribute with the same name.", 

270 UserWarning, 

271 stacklevel=find_stack_level(), 

272 ) 

273 setattr(cls, name, CachedAccessor(name, accessor)) 

274 cls._accessors.add(name) 

275 return accessor 

276 

277 return decorator 

278 

279 

280@doc(_register_accessor, klass="DataFrame") 

281def register_dataframe_accessor(name): 

282 from pandas import DataFrame 

283 

284 return _register_accessor(name, DataFrame) 

285 

286 

287@doc(_register_accessor, klass="Series") 

288def register_series_accessor(name): 

289 from pandas import Series 

290 

291 return _register_accessor(name, Series) 

292 

293 

294@doc(_register_accessor, klass="Index") 

295def register_index_accessor(name): 

296 from pandas import Index 

297 

298 return _register_accessor(name, Index)