def combine_mapped_with_other(self, other, np_func): """Combine `MappedArray` with other compatible object. If other object is also `MappedArray`, their `id_arr` and `col_arr` must match.""" if isinstance(other, MappedArray): checks.assert_array_equal(self.id_arr, other.id_arr) checks.assert_array_equal(self.col_arr, other.col_arr) other = other.values return self.copy(mapped_arr=np_func(self.values, other))
def make_symmetric(arg): """Make `arg` symmetric. The index and columns of the resulting DataFrame will be identical. Requires the index and columns to have the same number of levels. ## Example ```python-repl >>> import pandas as pd >>> from vectorbt.base.reshape_fns import make_symmetric >>> df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['c', 'd']) >>> make_symmetric(df) a b c d a NaN NaN 1.0 2.0 b NaN NaN 3.0 4.0 c 1.0 3.0 NaN NaN d 2.0 4.0 NaN NaN ``` """ checks.assert_type(arg, (pd.Series, pd.DataFrame)) arg = to_2d(arg) if isinstance(arg.index, pd.MultiIndex) or isinstance(arg.columns, pd.MultiIndex): checks.assert_type(arg.index, pd.MultiIndex) checks.assert_type(arg.columns, pd.MultiIndex) checks.assert_array_equal(arg.index.nlevels, arg.columns.nlevels) names1, names2 = tuple(arg.index.names), tuple(arg.columns.names) else: names1, names2 = arg.index.name, arg.columns.name if names1 == names2: new_name = names1 else: if isinstance(arg.index, pd.MultiIndex): new_name = tuple(zip(*[names1, names2])) else: new_name = (names1, names2) idx_vals = list(dict.fromkeys(np.concatenate((arg.index, arg.columns)))) arg = arg.copy() if isinstance(arg.index, pd.MultiIndex): unique_index = pd.MultiIndex.from_tuples(idx_vals, names=new_name) arg.index.names = new_name arg.columns.names = new_name else: unique_index = pd.Index(idx_vals, name=new_name) arg.index.name = new_name arg.columns.name = new_name df_out = pd.DataFrame(index=unique_index, columns=unique_index) df_out.loc[:, :] = arg df_out[df_out.isnull()] = arg.transpose() return df_out
def combine_mapped_with_other( self: MappedArrayT, other: tp.Union["MappedArray", tp.ArrayLike], np_func: tp.Callable[[tp.ArrayLike, tp.ArrayLike], tp.Array1d] ) -> MappedArrayT: """Combine `MappedArray` with other compatible object. If other object is also `MappedArray`, their `id_arr` and `col_arr` must match.""" if isinstance(other, MappedArray): checks.assert_array_equal(self.id_arr, other.id_arr) checks.assert_array_equal(self.col_arr, other.col_arr) other = other.values return self.replace(mapped_arr=np_func(self.values, other))
def test_assert_array_equal(self): index = ['x', 'y', 'z'] columns = ['a', 'b', 'c'] checks.assert_array_equal(np.array([1, 2, 3]), np.array([1, 2, 3])) checks.assert_array_equal(pd.Series([1, 2, 3], index=index), pd.Series([1, 2, 3], index=index)) checks.assert_array_equal(pd.DataFrame([[1, 2, 3]], columns=columns), pd.DataFrame([[1, 2, 3]], columns=columns)) with pytest.raises(Exception) as e_info: checks.assert_array_equal(np.array([1, 2]), np.array([1, 2, 3]))
def make_symmetric(arg: tp.SeriesFrame, sort: bool = True) -> tp.Frame: """Make `arg` symmetric. The index and columns of the resulting DataFrame will be identical. Requires the index and columns to have the same number of levels. Pass `sort=False` if index and columns should not be sorted, but concatenated and get duplicates removed. Usage: ```pycon >>> import pandas as pd >>> from vectorbt.base.reshape_fns import make_symmetric >>> df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['c', 'd']) >>> make_symmetric(df) a b c d a NaN NaN 1.0 2.0 b NaN NaN 3.0 4.0 c 1.0 3.0 NaN NaN d 2.0 4.0 NaN NaN ``` """ checks.assert_instance_of(arg, (pd.Series, pd.DataFrame)) df: tp.Frame = to_2d(arg) if isinstance(df.index, pd.MultiIndex) or isinstance( df.columns, pd.MultiIndex): checks.assert_instance_of(df.index, pd.MultiIndex) checks.assert_instance_of(df.columns, pd.MultiIndex) checks.assert_array_equal(df.index.nlevels, df.columns.nlevels) names1, names2 = tuple(df.index.names), tuple(df.columns.names) else: names1, names2 = df.index.name, df.columns.name if names1 == names2: new_name = names1 else: if isinstance(df.index, pd.MultiIndex): new_name = tuple(zip(*[names1, names2])) else: new_name = (names1, names2) if sort: idx_vals = np.unique(np.concatenate((df.index, df.columns))).tolist() else: idx_vals = list(dict.fromkeys(np.concatenate((df.index, df.columns)))) df_index = df.index.copy() df_columns = df.columns.copy() if isinstance(df.index, pd.MultiIndex): unique_index = pd.MultiIndex.from_tuples(idx_vals, names=new_name) df_index.names = new_name df_columns.names = new_name else: unique_index = pd.Index(idx_vals, name=new_name) df_index.name = new_name df_columns.name = new_name df = df.copy(deep=False) df.index = df_index df.columns = df_columns df_out_dtype = np.promote_types(df.values.dtype, np.min_scalar_type(np.nan)) df_out = pd.DataFrame(index=unique_index, columns=unique_index, dtype=df_out_dtype) df_out.loc[:, :] = df df_out[df_out.isnull()] = df.transpose() return df_out