Construct a pandas MultiIndex from `columns` and column index metadata in `column_indexes`. Parameters ---------- columns : List[pd.Index] The columns coming from a pyarrow.Table column_indexes : List[Dict[str, str]] The column index metadata deserialized from th
(columns, column_indexes)
| 1139 | |
| 1140 | |
| 1141 | def _reconstruct_columns_from_metadata(columns, column_indexes): |
| 1142 | """Construct a pandas MultiIndex from `columns` and column index metadata |
| 1143 | in `column_indexes`. |
| 1144 | |
| 1145 | Parameters |
| 1146 | ---------- |
| 1147 | columns : List[pd.Index] |
| 1148 | The columns coming from a pyarrow.Table |
| 1149 | column_indexes : List[Dict[str, str]] |
| 1150 | The column index metadata deserialized from the JSON schema metadata |
| 1151 | in a :class:`~pyarrow.Table`. |
| 1152 | |
| 1153 | Returns |
| 1154 | ------- |
| 1155 | result : MultiIndex |
| 1156 | The index reconstructed using `column_indexes` metadata with levels of |
| 1157 | the correct type. |
| 1158 | |
| 1159 | Notes |
| 1160 | ----- |
| 1161 | * Part of :func:`~pyarrow.pandas_compat.table_to_blockmanager` |
| 1162 | """ |
| 1163 | pd = _pandas_api.pd |
| 1164 | # Get levels and labels, and provide sane defaults if the index has a |
| 1165 | # single level to avoid if/else spaghetti. |
| 1166 | levels = getattr(columns, 'levels', None) or [columns] |
| 1167 | labels = getattr(columns, 'codes', None) or [None] |
| 1168 | |
| 1169 | # Convert each level to the dtype provided in the metadata |
| 1170 | levels_dtypes = [ |
| 1171 | (level, col_index.get('pandas_type', str(level.dtype)), |
| 1172 | col_index.get('numpy_type', None)) |
| 1173 | for level, col_index in zip_longest( |
| 1174 | levels, column_indexes, fillvalue={} |
| 1175 | ) |
| 1176 | ] |
| 1177 | |
| 1178 | new_levels = [] |
| 1179 | encoder = operator.methodcaller('encode', 'UTF-8') |
| 1180 | |
| 1181 | for level, pandas_dtype, numpy_dtype in levels_dtypes: |
| 1182 | dtype = _pandas_type_to_numpy_type(pandas_dtype) |
| 1183 | # Since our metadata is UTF-8 encoded, Python turns things that were |
| 1184 | # bytes into unicode strings when json.loads-ing them. We need to |
| 1185 | # convert them back to bytes to preserve metadata. |
| 1186 | if dtype == np.bytes_: |
| 1187 | level = level.map(encoder) |
| 1188 | # ARROW-13756: if index is timezone aware DataTimeIndex |
| 1189 | elif pandas_dtype == "datetimetz": |
| 1190 | tz = pa.lib.string_to_tzinfo( |
| 1191 | column_indexes[0]['metadata']['timezone'], |
| 1192 | prefer_zoneinfo=_pandas_api.is_ge_v3()) |
| 1193 | level = pd.to_datetime(level, utc=True).tz_convert(tz) |
| 1194 | if _pandas_api.is_ge_v3(): |
| 1195 | # with pandas 3+, to_datetime returns a unit depending on the string |
| 1196 | # data, so we restore it to the original unit from the metadata |
| 1197 | level = level.as_unit(np.datetime_data(numpy_dtype)[0]) |
| 1198 | # GH-41503: if the column index was decimal, restore to decimal |
no test coverage detected