Title words in a column except certain words

Viewed 859

How could I title all words except the ones in the list, keep?

keep = ['for', 'any', 'a', 'vs']
df.col
 ``         
0    1. The start for one
1    2. Today's world any
2    3. Today's world vs. yesterday.

Expected Output:

     number   title
0     1       The Start for One
1     2       Today's World any
2     3       Today's World vs. Yesterday.


I tried

df['col'] = df.col.str.title().mask(~clean['col'].isin(keep))
4 Answers

Here is one way of doing with str.replace and passing the replacement function:

def replace(match):
    word = match.group(1)
    if word not in keep:
        return word.title()
    return word

df['title'] = df['title'].str.replace(r'(\w+)', replace)

   number                         title
0       1             The Start for One
1       2             Today'S World any
2       3  Today'S World vs. Yesterday.

First we create your number and title column. Then we use Series.explode to get a word per row. If the word is in keep we ignore it, else apply Series.str.title:

keep = ['for', 'any', 'a', 'vs']

# create 'number' and 'title' column
df[['number', 'title']] = df['col'].str.split(".", expand=True, n=1)
df = df.drop(columns='col')

# apply str.title if not in keep
words = df['title'].str.split().explode()
words = words.str.replace(".", "", regex=False)
words = words.mask(words.isin(keep)).str.title().fillna(words)
df['title'] = words.groupby(level=0).agg(" ".join)

Output

  number                         title
0      1             The Start for One
1      2             Today'S World any
2      3  Today'S World vs. Yesterday.

You can create a function to accept a string and check against an iterable to decide whether to capitalize or not.

The function below does just that.

def keep_cap(string, it):
    '''
    Returns a generator by tokenizing a string and checking each word before capitalizing
    '''
    string_tokens = string.split()
    for i in string_tokens:
        if i in it:
            yield i
        else:
            yield i.capitalize()

With the function, you can apply it on any string such as:

' '.join(keep_cap('cap for cap any cap vs', keep))
>> 'Cap for Cap any Cap vs'

From that you can directly apply the function to the column and joining the generator shown below;

df = pd.DataFrame(["The start for one",
                   "Today's world any",
                   "Today's world vs. yesterday."], columns = ['sent'])

keep = ['for', 'any', 'a', 'vs']

df['sent'] = df['sent'].apply(lambda x: ' '.join(keep_cap(x,keep)) )

Output:

    sent
0   The Start for One
1   Today's World any
2   Today's World Vs. Yesterday.

This is a one line solution:

keep = ['for', 'any', 'a', 'vs']
        
df['col'] = df.col.str.split(" ").apply(lambda x: ' '.join([y.capitalize() if not (y in (keep)) else y for y in x]))
Related