【问题标题】:RegEx Results in new Column do not work properly (Pandas)新列中的正则表达式结果无法正常工作(熊猫)
【发布时间】:2021-10-02 10:26:23
【问题描述】:

我正在使用 Titanic 数据集来学习如何清理数据。我现在要做的是创建一个新列,其中包含 Name 列中的标题(先生、夫人、博士等),并根据社会阶层对它们进行分组:

Dr. – Doctor
Rev. – Clerical
Master. – Scholar 
Major., Col., Capt. – Military 
Mr., Mrs., Ms., Miss. – Commoners
Don., Sir., Mme., Mlle., Lady., Countess., Jonkheer. – Nobility 

我创建了一个函数,除了所有值都返回“Other”之外,它大部分都可以工作。

import os
import re

filename = os.path.join(os.path.dirname(__file__),'train.csv')
data = pd.read_csv(filename)

def create_title_column(data, colname):
    def search_title(x):
        title_search = re.search('\s([A-Za-z]+)\.', x)
        if title_search:
            title = title_search.groups(0)
            if title == 'Rev.':
                return 'Clerical'
            elif title == 'Master.':
                return 'Scholar'
            elif title == 'Dr.':
                return 'Doctor'
            elif title in ['Major.', 'Col.', 'Capt.']:
                return 'Military'
            elif title in ['Mr.','Mrs.','Ms.','Miss.']:
                return 'Commoner'
            elif title in ['Don.','Sir.','Mme.','Mlle.','Lady.','Countess.','Jonkheer.']:
                return 'Nobility'
            else:
                return 'Other'

    return_titles = data[colname].apply(search_title)
    dict_title = {1: 'Clerical', 2:'Scholar', 3:'Doctor', 4:'Military',5:'Commoner',6:'Nobility'}
    return return_titles.replace(dict_title)
data['Title'] = create_title_column(data, 'Name')

print(data.head(10))

我不知道我做错了什么。此外,如果您知道更好、更简洁的方法,欢迎您向我展示。任何帮助都会很棒,所以提前感谢您的宝贵时间。

您可以在此处下载泰坦尼克号数据集: https://www.kaggle.com/hesh97/titanicdataset-traincsv

【问题讨论】:

  • 您包含前导空格。
  • 嘿@garagnoth,感谢您的参与。我添加了 re.search('\s[A-Za-z]+\.', x),但它仍然无法正常工作跨度>
  • 我在下面提供了我的版本。你需要这样做title_search.groups(0).strip()

标签: python regex pandas


【解决方案1】:

IIUC:

import pandas as pd
import re

从本地存储读取数据

data = pd.read_csv('titanic_train.csv')

定义函数:

def search_title(x):
    if x == 'Rev.':
        return 'Clerical'
    if x == 'Mr':
        return 'Commoner'
    elif x == 'Master.':
        return 'Scholar'
    elif x == 'Dr.':
        return 'Doctor'
    elif x in ['Major.', 'Col.', 'Capt.']:
        return 'Military'
    elif x in ['Mr.','Mrs.','Ms.','Miss.']:
        return 'Commoner'
    elif x in ['Don.','Sir.','Mme.','Mlle.','Lady.','Countess.','Jonkheer.']:
        return 'Nobility'
    else:
        return 'Other'

两个步骤适用(因为我是初学者,更容易阅读):

data['Title'] = data['Name'].apply(lambda x: re.search(' ([A-Za-z]+)\.', x).group(0))
data['Title'] = data['Title'].apply(lambda x: search_title(x.strip()))

输出:

【讨论】:

  • 大声笑比我想要的要简单得多。它工作得很好!但是,还有另一个问题,但我认为这与代码无关。出于某种原因,它也将贵族视为平民。例如,如果您转到实际的 csv 文件并搜索 Lady,您也会得到所有 Misses。不知道为什么。
  • 嗯,在火车数据上,只有 1 位女士 (Duff Gordon, Lady. (Lucille Christiana Sutherland) (“Mrs Morgan”)),并且正确标记为 Nobility。
  • 好的,我需要查看文件。但我需要先睡觉哈哈。这里是凌晨 4 点。明天我会继续这个,并会告诉你。感谢您的帮助!
【解决方案2】:

numpyselect 为您提供的任务提供了一个非常好的功能。这是一个矢量化的解决方案,它有助于保持你的代码干净。所以我不鼓励为你的任务定义一个函数。

我会首先从名称列中提取标题(即“先生”、“小姐”、“文员”......)

在末尾链接str.stip() 以消除不需要的空格很重要。

# Grab the titles from the Name column
df['title']=df.Name.str.split(',').str[1].str.split('.').str[0].str.strip()
>>> df['title'].value_counts()

 Mr              517
 Miss            182
 Mrs             125
 Master           40
 Dr                7
 Rev               6
 Mlle              2
 Major             2
 Col               2
 Ms                1
 Don               1
 Capt              1
 Sir               1
 Lady              1
 the Countess      1
 Jonkheer          1
 Mme               1
Name: title, dtype: int64

在下一段代码中,我指定当前标题是什么,以及每个标题应该更改为什么。请记住,顺序很重要(即 Rev 将是 Clerical,Master 将是 Scholar 等)

# Import numpy package
import numpy as np

# Set the current titles that you have in the column
current_titles  = [
              df['title'] == 'Rev',\
              df['title'] == 'Master',\
              df['title'] == 'Dr',\
              df['title'].isin(['Major', 'Col', 'Capt']),\
              df['title'].isin(['Mr','Mrs','Ms','Miss']),\
              df['title'].isin(['Don','Sir','Mme','Mlle','Lady','Countess','Jonkheer'])\
              ]
# Set the new title to each title.
title_changes     = ['Clerical','Scholar','Doctor','Military','Commoner','Nobility']

指定 conditions (current_titles) 和 choices (title_changes),这是 np.select 采用的 3 个参数中的 2 个,您可以使用更新的数据创建新列:

df["title_refined"] = np.select(current_titles, title_changes, default="Other")

打印 3 列,以清楚地说明差异,产生您的预期输出:

>>> df[['Name','title','title_refined']]

                                                  Name title title_refined
0                              Braund, Mr. Owen Harris    Mr      Commoner
1    Cumings, Mrs. John Bradley (Florence Briggs Th...   Mrs      Commoner
2                               Heikkinen, Miss. Laina  Miss      Commoner
3         Futrelle, Mrs. Jacques Heath (Lily May Peel)   Mrs      Commoner
4                             Allen, Mr. William Henry    Mr      Commoner
..                                                 ...   ...           ...
886                              Montvila, Rev. Juozas   Rev      Clerical
887                       Graham, Miss. Margaret Edith  Miss      Commoner
888           Johnston, Miss. Catherine Helen "Carrie"  Miss      Commoner
889                              Behr, Mr. Karl Howell    Mr      Commoner
890                                Dooley, Mr. Patrick    Mr      Commoner

【讨论】:

  • 嘿 sopholes,这是一个不错的!不过,我需要检查我的 numpy。我将尝试复制您的答案并让您知道,但是,我看到许多单元格的输出显示为“其他”。我的原始代码和@garagnoth 的版本也有问题。例如,出于某种原因,所有贵族仍然阅读为平民。女士被读作小姐。
  • 我已经编辑了我的答案,“其他”太多的原因是我打错了标题。现在已经纠正了。你是什​​么意思贵族仍然在阅读作为一个平民?贵族和平民目前在标题列中不存在,它们是您的新更改..
  • 没关系,这是我的错误。这里太晚了。我需要睡眠。我明天会复制你的代码,然后会回复你。与此同时,我会赞成你的回答。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2012-05-25
  • 2016-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多