Skip to content

Commit ad3e5be

Browse files
committed
BUG: Preserve categorical dtypes when melting (pandas-dev#15853)
Add support for tile and not simply repeat.
1 parent 2d4dd50 commit ad3e5be

File tree

5 files changed

+95
-3
lines changed

5 files changed

+95
-3
lines changed

pandas/compat/numpy/function.py

+4
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ def validate_cum_func_with_skipna(skipna, args, kwargs, name):
211211
validate_repeat = CompatValidator(REPEAT_DEFAULTS, fname='repeat',
212212
method='both', max_fname_arg_count=1)
213213

214+
TILE_DEFAULTS = dict(axis=None)
215+
validate_tile = CompatValidator(TILE_DEFAULTS, fname='tile',
216+
method='both', max_fname_arg_count=1)
217+
214218
ROUND_DEFAULTS = dict(out=None)
215219
validate_round = CompatValidator(ROUND_DEFAULTS, fname='round',
216220
method='both', max_fname_arg_count=1)

pandas/core/arrays/categorical.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -2325,12 +2325,25 @@ def repeat(self, repeats, *args, **kwargs):
23252325
See also
23262326
--------
23272327
numpy.ndarray.repeat
2328-
2328+
Categorical.tile
23292329
"""
23302330
nv.validate_repeat(args, kwargs)
23312331
codes = self._codes.repeat(repeats)
23322332
return self._constructor(values=codes, dtype=self.dtype, fastpath=True)
23332333

2334+
def tile(self, reps, *args, **kwargs):
2335+
"""
2336+
Tile elements of a Categorical.
2337+
2338+
See also
2339+
--------
2340+
numpy.tile
2341+
Categorical.repeat
2342+
"""
2343+
nv.validate_tile(args, kwargs)
2344+
codes = np.tile(self._codes, reps)
2345+
return self._constructor(values=codes, dtype=self.dtype, fastpath=True)
2346+
23342347
# Implement the ExtensionArray interface
23352348
@property
23362349
def _can_hold_na(self):

pandas/core/indexes/base.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ def _simple_new(cls, values, name=None, dtype=None, **kwargs):
546546
def _shallow_copy(self, values=None, **kwargs):
547547
if values is None:
548548
values = self.values
549+
549550
attributes = self._get_attributes_dict()
550551
attributes.update(kwargs)
551552
if not len(values) and 'dtype' not in kwargs:
@@ -557,7 +558,6 @@ def _shallow_copy(self, values=None, **kwargs):
557558
# `self.values` returns `self` for tz-aware, so we need to unwrap
558559
# more specifically
559560
values = values.asi8
560-
561561
return self._simple_new(values, **attributes)
562562

563563
def _shallow_copy_with_infer(self, values, **kwargs):
@@ -822,6 +822,7 @@ def repeat(self, repeats, *args, **kwargs):
822822
--------
823823
Series.repeat : Equivalent function for Series
824824
numpy.repeat : Underlying implementation
825+
Series.tile : repeat the entire index as a group, not by element
825826
826827
Examples
827828
--------
@@ -836,6 +837,47 @@ def repeat(self, repeats, *args, **kwargs):
836837
nv.validate_repeat(args, kwargs)
837838
return self._shallow_copy(self._values.repeat(repeats))
838839

840+
def tile(self, reps, *args, **kwargs):
841+
"""
842+
Tile elements of an Index.
843+
844+
Returns a new index constructed by repeating the current index
845+
the number of times given by reps.
846+
847+
.. versionadded:: 0.24.0
848+
849+
Parameters
850+
----------
851+
reps : int
852+
The number of repetitions of the element groups.
853+
**kwargs
854+
Additional keywords have no effect but might be accepted for
855+
compatibility with numpy.
856+
857+
Returns
858+
-------
859+
pandas.Index
860+
Newly created Index with tiled elements.
861+
862+
See Also
863+
--------
864+
Series.tile : Equivalent function for Series
865+
numpy.tile : Underlying implementation
866+
Series.repeat : repeat the index element by element, not as a group
867+
868+
Examples
869+
--------
870+
>>> idx = pd.Index([1, 2, 3])
871+
>>> idx
872+
Int64Index([1, 2, 3], dtype='int64')
873+
>>> idx.tile(2)
874+
Int64Index([1, 2, 3, 1, 2, 3], dtype='int64')
875+
>>> idx.tile(3)
876+
Int64Index([1, 2, 3, 1, 2, 3, 1, 2, 3], dtype='int64')
877+
"""
878+
nv.validate_tile(args, kwargs)
879+
return self._shallow_copy(np.tile(self._values[:], reps))
880+
839881
_index_shared_docs['where'] = """
840882
.. versionadded:: 0.19.0
841883

pandas/core/indexes/datetimelike.py

+12
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,18 @@ def repeat(self, repeats, *args, **kwargs):
638638
return self._shallow_copy(self.asi8.repeat(repeats),
639639
freq=freq)
640640

641+
def tile(self, reps, *args, **kwargs):
642+
"""
643+
Analogous to numpy.tile
644+
"""
645+
nv.validate_tile(args, kwargs)
646+
if is_period_dtype(self):
647+
freq = self.freq
648+
else:
649+
freq = None
650+
return self._shallow_copy(np.tile(self.asi8, reps),
651+
freq=freq)
652+
641653
@Appender(_index_shared_docs['where'] % _index_doc_kwargs)
642654
def where(self, cond, other=None):
643655
other = _ensure_datetimelike_to_i8(other, to_utc=True)

pandas/core/series.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -989,11 +989,12 @@ def _set_values(self, key, value):
989989

990990
def repeat(self, repeats, *args, **kwargs):
991991
"""
992-
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
992+
Repeat elements of a Series. Refer to `numpy.ndarray.repeat`
993993
for more information about the `repeats` argument.
994994
995995
See also
996996
--------
997+
pd.Series.tile
997998
numpy.ndarray.repeat
998999
"""
9991000
nv.validate_repeat(args, kwargs)
@@ -1002,6 +1003,26 @@ def repeat(self, repeats, *args, **kwargs):
10021003
return self._constructor(new_values,
10031004
index=new_index).__finalize__(self)
10041005

1006+
def tile(self, reps, *args, **kwargs):
1007+
"""
1008+
Tile elements of a Series. Refer to `numpy.tile`
1009+
for more information about the `reps` argument.
1010+
1011+
See also
1012+
--------
1013+
pd.Series.repeat
1014+
numpy.tile
1015+
"""
1016+
nv.validate_tile(args, kwargs)
1017+
new_index = self.index.tile(reps)
1018+
if is_categorical_dtype(self.dtype):
1019+
return Categorical.from_codes(np.tile(self.cat.codes, reps),
1020+
categories=self.cat.categories,
1021+
ordered=self.cat.ordered)
1022+
new_values = np.tile(self._values, reps)
1023+
return self._constructor(new_values,
1024+
index=new_index).__finalize__(self)
1025+
10051026
def get_value(self, label, takeable=False):
10061027
"""Quickly retrieve single value at passed index label
10071028

0 commit comments

Comments
 (0)