Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pandas/core/internals/base.py: 50%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

88 statements  

1""" 

2Base class for the internal managers. Both BlockManager and ArrayManager 

3inherit from this class. 

4""" 

5from __future__ import annotations 

6 

7from typing import ( 

8 Literal, 

9 TypeVar, 

10 final, 

11) 

12 

13import numpy as np 

14 

15from pandas._typing import ( 

16 ArrayLike, 

17 AxisInt, 

18 DtypeObj, 

19 Shape, 

20) 

21from pandas.errors import AbstractMethodError 

22 

23from pandas.core.dtypes.cast import ( 

24 find_common_type, 

25 np_can_hold_element, 

26) 

27 

28from pandas.core.base import PandasObject 

29from pandas.core.indexes.api import ( 

30 Index, 

31 default_index, 

32) 

33 

34T = TypeVar("T", bound="DataManager") 

35 

36 

37class DataManager(PandasObject): 

38 # TODO share more methods/attributes 

39 

40 axes: list[Index] 

41 

42 @property 

43 def items(self) -> Index: 

44 raise AbstractMethodError(self) 

45 

46 @final 

47 def __len__(self) -> int: 

48 return len(self.items) 

49 

50 @property 

51 def ndim(self) -> int: 

52 return len(self.axes) 

53 

54 @property 

55 def shape(self) -> Shape: 

56 return tuple(len(ax) for ax in self.axes) 

57 

58 @final 

59 def _validate_set_axis(self, axis: AxisInt, new_labels: Index) -> None: 

60 # Caller is responsible for ensuring we have an Index object. 

61 old_len = len(self.axes[axis]) 

62 new_len = len(new_labels) 

63 

64 if axis == 1 and len(self.items) == 0: 

65 # If we are setting the index on a DataFrame with no columns, 

66 # it is OK to change the length. 

67 pass 

68 

69 elif new_len != old_len: 

70 raise ValueError( 

71 f"Length mismatch: Expected axis has {old_len} elements, new " 

72 f"values have {new_len} elements" 

73 ) 

74 

75 def reindex_indexer( 

76 self: T, 

77 new_axis, 

78 indexer, 

79 axis: AxisInt, 

80 fill_value=None, 

81 allow_dups: bool = False, 

82 copy: bool = True, 

83 only_slice: bool = False, 

84 ) -> T: 

85 raise AbstractMethodError(self) 

86 

87 @final 

88 def reindex_axis( 

89 self: T, 

90 new_index: Index, 

91 axis: AxisInt, 

92 fill_value=None, 

93 only_slice: bool = False, 

94 ) -> T: 

95 """ 

96 Conform data manager to new index. 

97 """ 

98 new_index, indexer = self.axes[axis].reindex(new_index) 

99 

100 return self.reindex_indexer( 

101 new_index, 

102 indexer, 

103 axis=axis, 

104 fill_value=fill_value, 

105 copy=False, 

106 only_slice=only_slice, 

107 ) 

108 

109 def _equal_values(self: T, other: T) -> bool: 

110 """ 

111 To be implemented by the subclasses. Only check the column values 

112 assuming shape and indexes have already been checked. 

113 """ 

114 raise AbstractMethodError(self) 

115 

116 @final 

117 def equals(self, other: object) -> bool: 

118 """ 

119 Implementation for DataFrame.equals 

120 """ 

121 if not isinstance(other, DataManager): 

122 return False 

123 

124 self_axes, other_axes = self.axes, other.axes 

125 if len(self_axes) != len(other_axes): 

126 return False 

127 if not all(ax1.equals(ax2) for ax1, ax2 in zip(self_axes, other_axes)): 

128 return False 

129 

130 return self._equal_values(other) 

131 

132 def apply( 

133 self: T, 

134 f, 

135 align_keys: list[str] | None = None, 

136 **kwargs, 

137 ) -> T: 

138 raise AbstractMethodError(self) 

139 

140 @final 

141 def isna(self: T, func) -> T: 

142 return self.apply("apply", func=func) 

143 

144 # -------------------------------------------------------------------- 

145 # Consolidation: No-ops for all but BlockManager 

146 

147 def is_consolidated(self) -> bool: 

148 return True 

149 

150 def consolidate(self: T) -> T: 

151 return self 

152 

153 def _consolidate_inplace(self) -> None: 

154 return 

155 

156 

157class SingleDataManager(DataManager): 

158 @property 

159 def ndim(self) -> Literal[1]: 

160 return 1 

161 

162 @final 

163 @property 

164 def array(self) -> ArrayLike: 

165 """ 

166 Quick access to the backing array of the Block or SingleArrayManager. 

167 """ 

168 # error: "SingleDataManager" has no attribute "arrays"; maybe "array" 

169 return self.arrays[0] # type: ignore[attr-defined] 

170 

171 def setitem_inplace(self, indexer, value) -> None: 

172 """ 

173 Set values with indexer. 

174 

175 For Single[Block/Array]Manager, this backs s[indexer] = value 

176 

177 This is an inplace version of `setitem()`, mutating the manager/values 

178 in place, not returning a new Manager (and Block), and thus never changing 

179 the dtype. 

180 """ 

181 arr = self.array 

182 

183 # EAs will do this validation in their own __setitem__ methods. 

184 if isinstance(arr, np.ndarray): 

185 # Note: checking for ndarray instead of np.dtype means we exclude 

186 # dt64/td64, which do their own validation. 

187 value = np_can_hold_element(arr.dtype, value) 

188 

189 if isinstance(value, np.ndarray) and value.ndim == 1 and len(value) == 1: 

190 # NumPy 1.25 deprecation: https://github.com/numpy/numpy/pull/10615 

191 value = value[0, ...] 

192 

193 arr[indexer] = value 

194 

195 def grouped_reduce(self, func): 

196 arr = self.array 

197 res = func(arr) 

198 index = default_index(len(res)) 

199 

200 mgr = type(self).from_array(res, index) 

201 return mgr 

202 

203 @classmethod 

204 def from_array(cls, arr: ArrayLike, index: Index): 

205 raise AbstractMethodError(cls) 

206 

207 

208def interleaved_dtype(dtypes: list[DtypeObj]) -> DtypeObj | None: 

209 """ 

210 Find the common dtype for `blocks`. 

211 

212 Parameters 

213 ---------- 

214 blocks : List[DtypeObj] 

215 

216 Returns 

217 ------- 

218 dtype : np.dtype, ExtensionDtype, or None 

219 None is returned when `blocks` is empty. 

220 """ 

221 if not len(dtypes): 

222 return None 

223 

224 return find_common_type(dtypes)