Preserve full surname, get initials of first name (and middle name if some) in pandas column

Viewed 726

I have a pandas Dataframe with a column expressing the surname and name of several tennis players like the following one:

   | Player              | 
   |---------------------|
0  | 'Roddick Andy'      |
1  | 'Federer Roger'     |
2  | 'Tsonga Jo Wilfred  |

I want to keep the full surname and get the initial of the name and middle name if there is. So the pandas column should look like the following one:

   | Player            | 
   |-------------------|
0  | 'Roddick A.'      |
1  | 'Federer R.'      |
2  | 'Tsonga J.W.'     | N.B. J.W. with no space

Does anyone have suggestions? Thank!

3 Answers

Here's an approach with str.extractall and groupby:

(df.Player
  .str.extractall('(?P<Surname>\w*)\s(?P<Name>\w*)')
  .groupby(level=0)
  .agg({'Surname':'first',
        'Name': lambda x: x.str[0].add('.').sum()
        })
  .agg(' '.join, axis=1)
)

Output:

0     Roddick A.
1     Federer R.
2    Tsonga J.W.
dtype: object

You could make use of df.replace as follows:

import pandas as pd 
df = pd.DataFrame({'Player':['Roddick Andy', 'Federer Roger', 'Tsonga Jo Wilfred']})
df = df.replace(r'(?<=\s\w)\w+\s?', value = '.', regex = True)
print(df)

Regular Expression: (?<=\s\w)\w+\s?

  • \w+ - Match any word character at least ones
  • (?<=\s\w) - Positive lookbehind (step one must be preceded by a single whitespace and any single wordcharacter
  • \s? - Step one is followed by an optional whitespace.

Results:

        Player
0   Roddick A.
1   Federer R.
2  Tsonga J.W.

EDIT:

Option 1:

To swap around the two initials, we could (to maintain the idea of using regular expressions) add another df.replace, with a second regular expression.

import pandas as pd 
df = pd.DataFrame({'Player':['Roddick Andy', 'Federer Roger', 'Tsonga Jo Wilfred']})
df = df.replace(r'(?<=\s\w)\w+\s?', value = '.', regex = True)
df = df.replace(r'(.(?=\.)\.)(.(?=\.)\.)?', value = r'\2\1', regex = True)
print(df)

Regular Expression: (.(?=\.)\.)(.(?=\.)\.)?

  • (.(?=\.)\.) - Capture group 1: Match any characer (.) that is followed by (positive lookahead) a literal dot, including matching a dot.
  • (.(?=\.)\.)? - Capture group 2: Optional (?) capturing group matching the exact same pattern as capture group 1.

The above isn't very dynamic. But it will work fine when there are a max of two initials. It's also possible to append on it rather easily, however the below option would be more dynamic.

Option 2:

Before you continue, may I mention I'm rather new to Python so I'm sure that what I done here can be shortened/done much simpler.

The idea here is to split and apend the dataframe, then reverse all but the first column to then join them back together into a single column in the dataframe, where we then can apply the df.replace again.

import pandas as pd
df = pd.DataFrame({'Player':['Roddick Andy', 'Federer Roger', 'Tsonga Jo Wilfred']})
df = df.Player.str.split(None, expand=True).iloc[:, ::-1]
df = df[[list(df.columns)[-1]] + list(df.columns)[:-1]]
df = df[df.columns[0:]].apply(lambda x: ' '.join(x.dropna().astype(str)), axis=1)
df = df.replace(r'(?<=\s\w)\w+\s?', value = '.', regex = True)
print(df)

Results:

        Player
0   Roddick A.
1   Federer R.
2  Tsonga W.J.

Assuming every lastname is one word last name:

def shorten_name(name):
    name_as_list = name.split(" ")
    last_name = name_as_list[-1]
    first_names = ".".join([w[0] for w in name_as_list[:-1]]) + "."
    return (last_name, first_names)

using like this:

last_name, first_names_shortened = shorten_name("Jo Wilfried Tsonga")
Related