From 904bad3a7e708feb0f9ea932ccd69bcf426d20c7 Mon Sep 17 00:00:00 2001 From: niklas_keck Date: Tue, 5 Sep 2017 12:22:58 +0200 Subject: [PATCH 1/2] ENH: Add optional argument keep_index to dataframe melt method Setting keep_index to True will reuse the original DataFrame index + names of melted columns as additional level. closes issue #17440 (cherry picked from commit 0c64bf0e5f145781c0f74fb93ffcd63a6d964bd9) --- pandas/core/frame.py | 6 ++++++ pandas/core/reshape/melt.py | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 5200ad0ba0d23..4946ce4e13eae 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -6312,6 +6312,10 @@ def unstack(self, level=-1, fill_value=None): Name to use for the 'value' column. col_level : int or str, optional If columns are a MultiIndex then use this level to melt. + keep_index : boolean, optional, default False + If True, the original index is reused. + In the resulting MulitIndex the names of the unpivoted columns + are added as an additional level to ensure uniqueness. Returns ------- @@ -6396,6 +6400,7 @@ def melt( var_name=None, value_name="value", col_level=None, + keep_index=False, ): from pandas.core.reshape.melt import melt @@ -6406,6 +6411,7 @@ def melt( var_name=var_name, value_name=value_name, col_level=col_level, + keep_index=keep_index, ) # ---------------------------------------------------------------------- diff --git a/pandas/core/reshape/melt.py b/pandas/core/reshape/melt.py index 6f2e264f1a4d0..f16f127f7b6c6 100644 --- a/pandas/core/reshape/melt.py +++ b/pandas/core/reshape/melt.py @@ -27,6 +27,7 @@ def melt( var_name=None, value_name="value", col_level=None, + keep_index=False, ): # TODO: what about the existing index? # If multiindex, gather names of columns on all level for checking presence @@ -116,7 +117,22 @@ def melt( # asanyarray will keep the columns as an Index mdata[col] = np.asanyarray(frame.columns._get_level_values(i)).repeat(N) - return frame._constructor(mdata, columns=mcolumns) + result = frame._constructor(mdata, columns=mcolumns) + + if keep_index: + orig_index_values = list(np.tile(frame.index.get_values(), K)) + + if len(frame.index.names) == len(set(frame.index.names)): + orig_index_names = frame.index.names + else: + orig_index_names = ["original_index_{i}".format(i=i) + for i in range(len(frame.index.names))] + + result[orig_index_names] = frame._constructor(orig_index_values) + + result = result.set_index(orig_index_names + list(var_name)) + + return result def lreshape(data, groups, dropna=True, label=None): From 706eebbe056720ac08d9d23a66bd6597888f498f Mon Sep 17 00:00:00 2001 From: Shyam Saladi Date: Tue, 8 Oct 2019 14:41:35 -0700 Subject: [PATCH 2/2] blacken --- pandas/core/reshape/melt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/reshape/melt.py b/pandas/core/reshape/melt.py index f16f127f7b6c6..c01f15f2682bd 100644 --- a/pandas/core/reshape/melt.py +++ b/pandas/core/reshape/melt.py @@ -125,8 +125,9 @@ def melt( if len(frame.index.names) == len(set(frame.index.names)): orig_index_names = frame.index.names else: - orig_index_names = ["original_index_{i}".format(i=i) - for i in range(len(frame.index.names))] + orig_index_names = [ + "original_index_{i}".format(i=i) for i in range(len(frame.index.names)) + ] result[orig_index_names] = frame._constructor(orig_index_values)