根据每个句子的第一个单词将 pandas 数据框列中的字符串列表分解为新列

Posted

技术标签:

【中文标题】根据每个句子的第一个单词将 pandas 数据框列中的字符串列表分解为新列【英文标题】:Break up a list of strings in a pandas dataframe column into new columns based on first word of each sentence 【发布时间】:2019-12-17 12:55:40 【问题描述】:

所以我有大约 40,000 行人员及其投诉。我正在尝试将它们分类到各自的列中进行分析,并为其他分析师 在我的公司,使用其他工具的人可以使用这些数据。

数据帧示例:

df = pd.DataFrame("person": [1, 2, 3], 
                   "problems": ["body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE); mind: stressed, tired", 
                                "soul: missing; mind: can't think; body: feels great(lifts weights), overweight(always bulking), missing a finger", 
                                "none"])
df     
╔═══╦════════╦══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║   ║ person ║                                                     problems                                                     ║
╠═══╬════════╬══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ 0 ║      1 ║ body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE); mind: stressed, tired                                         ║
║ 1 ║      2 ║ soul: missing; mind: can't think; body: feels great(lifts weights), overweight(always bulking), missing a finger ║
║ 2 ║      3 ║ none                                                                                                             ║
╚═══╩════════╩══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

期望的输出:

╔═══╦════════╦══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦════════════════════════════════════════════════════════════════════════════════╦═══════════════════════╦═══════════════╗
║   ║ person ║                                                     problems                                                     ║                                      body                                      ║         mind          ║     soul      ║
╠═══╬════════╬══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╬════════════════════════════════════════════════════════════════════════════════╬═══════════════════════╬═══════════════╣
║ 0 ║      1 ║ body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE); mind: stressed, tired                                         ║ body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE)                              ║ mind: stressed, tired ║ NaN           ║
║ 1 ║      2 ║ soul: missing; mind: can't think; body: feels great(lifts weights), overweight(always bulking), missing a finger ║ body: feels great(lifts weights), overweight(always bulking), missing a finger ║ mind: can't think     ║ soul: missing ║
║ 2 ║      3 ║ none                                                                                                             ║ NaN                                                                            ║ NaN                   ║ NaN           ║
╚═══╩════════╩══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩════════════════════════════════════════════════════════════════════════════════╩═══════════════════════╩═══════════════╝

我尝试过的事情/我现在所处的位置:

因此,我至少能够使用正则表达式语句将它们分开,该正则表达式语句似乎可以处理我的真实数据。

df.problems.str.extractall(r"(\b(?!(?: \b))[\w\s.()',:/-]+)")


+---+-------+--------------------------------------------------------------------------------+
|   |       |                                       0                                        |
+---+-------+--------------------------------------------------------------------------------+
|   | match |                                                                                |
| 0 | 0     | body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE)                              |
|   | 1     | mind: stressed, tired                                                          |
| 1 | 0     | soul: missing                                                                  |
|   | 1     | mind: can't think                                                              |
|   | 2     | body: feels great(lifts weights), overweight(always bulking), missing a finger |
| 2 | 0     | none                                                                           |
+---+-------+--------------------------------------------------------------------------------+

我是一个正则表达式初学者,所以我希望这可能会做得更好。我原来的正则表达式模式是r'([^;]+)',但我试图排除分号后的空格。

所以我很茫然。我玩过:

df.problems.str.extractall(r"(\b(?!(?: \b))[\w\s.()',:/-]+)").unstack(),在我的示例中“有效”(不会出错)。

但是用我的真实数据,我得到一个错误:"ValueError: Index contains duplicate entries, cannot reshape"

即使它适用于我的真实数据,我仍然需要弄清楚如何将这些“类别”(身体、思想、灵魂)放入指定的列中。

如果我能更好地表达这个问题,我可能会有更好的运气。我正在尝试在这里真正自学,因此即使它们不是完整的解决方案,我也会很感激任何线索。

我正在寻找一条线索,也许我可以通过 groupby 或 multiIndex 技术以某种方式做到这一点。对编程有点陌生,所以我仍然在黑暗中摸索。我将不胜感激任何人提供的任何提示或想法。谢谢!

编辑:我只想回过头来提一下我在使用@WeNYoBen 的解决方案时在我的真实数据"ValueError: Index contains duplicate entries, cannot reshape" 中遇到的错误:

(df.problems.str.extractall(r"(\b(?!(?: \b))[\w\s.()',:/-]+)")[0]
.str.split(':',expand=True)
.set_index(0,append=True)[1]
.unstack()
.groupby(level=0)
.first())

原来我有一些带有多个冒号的组。例如:

df = pd.DataFrame("person": [1, 2, 3], 
                   "problems": ["body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE); mind: stressed, energy: tired", 
                                "soul: missing; mind: can't think; body: feels great(lifts weights), overweight(always bulking), missing a finger", 
                                "none"])




╔═══╦════════╦══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║   ║ person ║                                                     problems                                                     ║
╠═══╬════════╬══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ 0 ║      1 ║ body: knee hurts(bad-pain), toes hurt(BIG/MIDDLE); mind: stressed, energy: tired                                 ║
║ 1 ║      2 ║ soul: missing; mind: can't think; body: feels great(lifts weights), overweight(always bulking), missing a finger ║
║ 2 ║      3 ║ none                                                                                                             ║
╚═══╩════════╩══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

查看第一行更新,反映我发现的边缘情况 ; mind: stressed, energy: tired

我可以通过更改我的正则表达式来解决此问题,以说明匹配的开头必须是字符串的开头或以分号开头。

splits = [r'(^)(.+?)[:]', r'(;)(.+?)[:]']
str.split('|'.join(splits)

在那之后,我只需要重新调整 set_index 部分就可以让@WeNYoBen 的有用解决方案发挥作用,所以我会坚持使用这个。

【问题讨论】:

我应该补充一点,昨天我确实弄清楚了如何在我的真实数据中获取所有这些分组的完整列表,总共大约 10 个。 sorted(df.problems.str.extractall(r'(^|;)(.+?)[:]').reset_index()[1].str.strip().unique()) 三个类别总是一样的吗?身体、思想、灵魂…… 如果它们总是三个类别,那么您可能会尝试从body ... ; 匹配每个类别。 df.problems.str.extractall(r"(\b(?!(?: \b))[\w\s.()',:/-]+)")[0].str.split(':',expand=True).set_index(0,append=True)[1].unstack().groupby(level=0).first() 你自己的函数抛光后比applt + lambda好 @sobek 我实际上将您的两种解决方案都用作垫脚石。试图解决另一个极端情况,我发现在一个短语中使用分号,在数万行中只出现了两次:/ 你们摇滚不过。这个周末学到了很多 【参考方案1】:

它并不优雅,但它完成了工作:

df['split'] = df.problems.str.split(';')
df['mind'] = df.split.apply(
    lambda x: ''.join([category for category in x if 'mind' in category]))
df['body'] = df.split.apply(
    lambda x: ''.join([category for category in x if 'body' in category]))
df['soul'] = df.split.apply(
    lambda x: ''.join([category for category in x if 'soul' in category]))
df.drop('split', inplace=True)

你可以换行

df[cat] = df.split.apply(lambda x: ''.join([category for category in x if cat in category])) 

在一个函数中并在您的数据帧上为每个cat 运行它(例如cats=['mind', 'body', 'soul', 'whathaveyou', 'etc.']


编辑

正如@ifly6 所指出的,用户输入的字符串中可能存在关键字交叉。为了安全起见,该函数应更改为

df[cat] = df.split.apply(lambda x: ''.join([category for category in x if category.startswith(cat)])) 

【讨论】:

非常感谢!我可以用这个!伙计,我真的需要花时间了解 lambda。 如果您有什么不明白的具体事情,请提出,我会尽力澄清我的答案。 感谢您的提议!总的来说,我对编程还是很陌生,我只需要从一开始就重新审视它。 如果字符串是“心灵:他认为他的身体变形了”怎么办? 请注意,我已经编辑了我的答案以处理这种极端情况(见底部)。

以上是关于根据每个句子的第一个单词将 pandas 数据框列中的字符串列表分解为新列的主要内容,如果未能解决你的问题,请参考以下文章

检查列表中的单词并在 pandas 数据框列中删除这些单词

javascript [All Caps]用于将句子中每个单词的第一个字母大写的脚本#vanilla #script

将数据框列中每个单词的首字母大写

将列表列表中的值映射到 Pandas 数据框列

根据列名删除 Pandas 数据框列

pandas:根据第一个字符映射新值