python - remove whitespace between two characters using re.sub

Viewed 793

I have a pair of columns, like so:

x = ["a b williams", "e g", "z z specialists"]
y = ["j j winston", "hb d party supplies", "t t ice cream"]
df = pd.DataFrame(x,y)

I would like to be able to remove the white space between two single characters using re.sub. I have tried the following:

re.sub("(?<=\\w\\b)"\\s"(?=\\w\\b)", "", df)

However, when I run the code, I get the following error.

SyntaxError: unexpected character after line continuation character

I'm unsure of what I am doing wrong. The desired result is:

jj winston             ab williams
hb d party supplies              eg
tt ice cream           zz specialists

Please advise. Any advice is appreciated.

3 Answers

You can use

(?<=\b[^\W\d_])\s(?=[^\W\d_]\b)
(?<=\b\w)\s(?=\w\b)

See the regex demo. Note the [^\W\d_] pattern matches any Unicode letter in Python re. \w matches Unicode letters, digits, _ and some diacritics and other connector punctuation.

Details

  • (?<=\b[^\W\d_]) - a positive lookbehind that matches a location that is immediately preceded with a single letter as a whole word (as it is prepended with a word boundary)
  • \s - a whitespace char
  • (?=[^\W\d_]\b) - a positive lookahead that matches a location that is immediately followed with a single letter as a whole word (as it is followed with a word boundary).

Here is a Pandas demo:

x = ["a b williams", "e g", "z z specialists"]
y = ["j j winston", "h d party supplies", "t t ice cream"]
df = pd.DataFrame(x,y)
rx = r'(?<=\b[^\W\d_])\s(?=[^\W\d_]\b)'
df.index = df.index.to_series().replace(rx, '', regex=True)
df = df.replace(rx, '', regex=True)
# => df
#                                 0
# jj winston            ab williams
# hd party supplies              eg
# tt ice cream       zz specialists

As DataFrame.replace with regex=True does not touch the index column, it must be handled separately, hence the df.index = df.index.to_series().replace(rx, '', regex=True) line of code is added.

Your regex is pretty close to the required and can be slightly modified as follows:

r'(?<=\b\w)(\s)(?=\w\b)'

Note to use the raw quote r'...' so that you don't need double \ for in the regex.

Regex Demo

Better compile the regex to speed up the processing as it is used multiple times

pattern = re.compile(r'(?<=\b\w)(\s)(?=\w\b)')

Then reuse your codes:

x = ["a b williams", "e g", "z z specialists"]
y = ["j j winston", "h d party supplies", "t t ice cream"]
df = pd.DataFrame(x,y)

Convert the index:

df.index = df.index.to_series().str.replace(pattern, '')

Convert the data column:

df[0] = df[0].str.replace(pattern, '')

Explanation of your errors:

  1. You cannot use re.sub directly on the whole pandas DataFrame
  2. Your regex contains 4 quotation marks " where the 2nd " ends the regex and so the subsequent portion of regex is treated as continuation line by the \ mark and the characters after it was considered invalid after continuation line

Using re.sub, I suggest the following:

# your lists    
x = ["a b williams", "e g", "z z specialists"]
y = ["j j winston", "hb d party supplies", "t t ice cream"]

# replacements
x = [re.sub(r'(\b\w)(\s)(\w\b)', r'\1\3', el) for el in x]
y = [re.sub(r'(\b\w)(\s)(\w\b)', r'\1\3', el) for el in y]

# pd dataframe after the process
df = pd.DataFrame(x,y)
Related