diff --git a/doc/source/whatsnew/v0.15.2.txt b/doc/source/whatsnew/v0.15.2.txt index c85a9376fc93f..f8b20eaace50e 100644 --- a/doc/source/whatsnew/v0.15.2.txt +++ b/doc/source/whatsnew/v0.15.2.txt @@ -69,6 +69,7 @@ Bug Fixes - ``io.data.Options`` now raises ``RemoteDataError`` when no expiry dates are available from Yahoo (:issue:`8761`). - ``Timedelta`` kwargs may now be numpy ints and floats (:issue:`8757`). - ``sql_schema`` now generates dialect appropriate ``CREATE TABLE`` statements (:issue:`8697`) +- ``slice`` string method now takes step into account (:issue:`8754`) diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 78780bc9618f7..2c2a98c0c5434 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -666,7 +666,7 @@ def str_split(arr, pat=None, n=None, return_type='series'): return res -def str_slice(arr, start=None, stop=None, step=1): +def str_slice(arr, start=None, stop=None, step=None): """ Slice substrings from each element in array @@ -674,6 +674,7 @@ def str_slice(arr, start=None, stop=None, step=1): ---------- start : int or None stop : int or None + step : int or None Returns ------- @@ -993,8 +994,8 @@ def center(self, width): return self._wrap_result(result) @copy(str_slice) - def slice(self, start=None, stop=None, step=1): - result = str_slice(self.series, start, stop) + def slice(self, start=None, stop=None, step=None): + result = str_slice(self.series, start, stop, step) return self._wrap_result(result) @copy(str_slice) diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py index 02808ebf0b340..a7d3c53c31e3d 100644 --- a/pandas/tests/test_strings.py +++ b/pandas/tests/test_strings.py @@ -628,6 +628,7 @@ def test_empty_str_methods(self): tm.assert_series_equal(empty_str, empty.str.center(42)) tm.assert_series_equal(empty_list, empty.str.split('a')) tm.assert_series_equal(empty_str, empty.str.slice(stop=1)) + tm.assert_series_equal(empty_str, empty.str.slice(step=1)) tm.assert_series_equal(empty_str, empty.str.strip()) tm.assert_series_equal(empty_str, empty.str.lstrip()) tm.assert_series_equal(empty_str, empty.str.rstrip()) @@ -922,6 +923,17 @@ def test_slice(self): exp = Series(['foo', 'bar', NA, 'baz']) tm.assert_series_equal(result, exp) + for start, stop, step in [(0, 3, -1), (None, None, -1), + (3, 10, 2), (3, 0, -1)]: + try: + result = values.str.slice(start, stop, step) + expected = Series([s[start:stop:step] if not isnull(s) else NA for s in + values]) + tm.assert_series_equal(result, expected) + except: + print('failed on %s:%s:%s' % (start, stop, step)) + raise + # mixed mixed = Series(['aafootwo', NA, 'aabartwo', True, datetime.today(), None, 1, 2.]) @@ -933,6 +945,10 @@ def test_slice(self): tm.assert_isinstance(rs, Series) tm.assert_almost_equal(rs, xp) + rs = Series(mixed).str.slice(2, 5, -1) + xp = Series(['oof', NA, 'rab', NA, NA, + NA, NA, NA]) + # unicode values = Series([u('aafootwo'), u('aabartwo'), NA, u('aabazqux')]) @@ -941,6 +957,10 @@ def test_slice(self): exp = Series([u('foo'), u('bar'), NA, u('baz')]) tm.assert_series_equal(result, exp) + result = values.str.slice(0, -1, 2) + exp = Series([u('afow'), u('abrw'), NA, u('abzu')]) + tm.assert_series_equal(result, exp) + def test_slice_replace(self): pass @@ -1151,6 +1171,10 @@ def test_string_slice_get_syntax(self): expected = s.str.slice(stop=3) assert_series_equal(result, expected) + result = s.str[2::-1] + expected = s.str.slice(start=2, step=-1) + assert_series_equal(result, expected) + def test_string_slice_out_of_bounds(self): s = Series([(1, 2), (1,), (3,4,5)])