Repository: pandas-dev/pandas

Issue:
Implement StringMethod slice_replace
The `StringMethod` `slice_replace` currently raises `NotImplementedError`. Couldn't find an issue for it, so opening this. PR with implementation in the works.

Could use thoughts on how to handle slices that return an empty string on some subset of strings - should the replacement string appear or not? This could happen when the input string is empty, the stop index is less than or equal to the start index (separate cases?), or both start and stop are greater than the length of the string.



Discussion:

Resolving patch:
diff --git a/pandas/core/strings.py b/pandas/core/strings.py
--- a/pandas/core/strings.py
+++ b/pandas/core/strings.py
@@ -687,15 +687,34 @@ def str_slice(arr, start=None, stop=None, step=None):
 
 def str_slice_replace(arr, start=None, stop=None, repl=None):
     """
+    Replace a slice of each string with another string.
 
     Parameters
     ----------
+    start : int or None
+    stop : int or None
+    repl : str or None
 
     Returns
     -------
     replaced : array
     """
-    raise NotImplementedError
+    if repl is None:
+        repl = ''
+
+    def f(x):
+        if x[start:stop] == '':
+            local_stop = start
+        else:
+            local_stop = stop
+        y = ''
+        if start is not None:
+            y += x[:start]
+        y += repl
+        if stop is not None:
+            y += x[local_stop:]
+        return y
+    return _na_map(f, arr)
 
 
 def str_strip(arr, to_strip=None):
@@ -998,9 +1017,10 @@ 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)
-    def slice_replace(self, i=None, j=None):
-        raise NotImplementedError
+    @copy(str_slice_replace)
+    def slice_replace(self, start=None, stop=None, repl=None):
+        result = str_slice_replace(self.series, start, stop, repl)
+        return self._wrap_result(result)
 
     @copy(str_decode)
     def decode(self, encoding, errors="strict"):


Test patch:
diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py
--- a/pandas/tests/test_strings.py
+++ b/pandas/tests/test_strings.py
@@ -963,7 +963,39 @@ def test_slice(self):
         tm.assert_series_equal(result, exp)
 
     def test_slice_replace(self):
-        pass
+        values = Series(['short', 'a bit longer', 'evenlongerthanthat', '', NA])
+
+        exp = Series(['shrt', 'a it longer', 'evnlongerthanthat', '', NA])
+        result = values.str.slice_replace(2, 3)
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['shzrt', 'a zit longer', 'evznlongerthanthat', 'z', NA])
+        result = values.str.slice_replace(2, 3, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['shzort', 'a zbit longer', 'evzenlongerthanthat', 'z', NA])
+        result = values.str.slice_replace(2, 2, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['shzort', 'a zbit longer', 'evzenlongerthanthat', 'z', NA])
+        result = values.str.slice_replace(2, 1, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['shorz', 'a bit longez', 'evenlongerthanthaz', 'z', NA])
+        result = values.str.slice_replace(-1, None, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['zrt', 'zer', 'zat', 'z', NA])
+        result = values.str.slice_replace(None, -2, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['shortz', 'a bit znger', 'evenlozerthanthat', 'z', NA])
+        result = values.str.slice_replace(6, 8, 'z')
+        tm.assert_series_equal(result, exp)
+
+        exp = Series(['zrt', 'a zit longer', 'evenlongzerthanthat', 'z', NA])
+        result = values.str.slice_replace(-10, 3, 'z')
+        tm.assert_series_equal(result, exp)
 
     def test_strip_lstrip_rstrip(self):
         values = Series(['  aa   ', ' bb \n', NA, 'cc  '])