【问题标题】:pandanic way of inserting df[col].str.extract() results back into original Pandas df immediately after the extraction column在提取列之后立即将 df[col].str.extract() 结果插入原始 Pandas df 的 pandanic 方式
【发布时间】:2021-05-29 19:17:09
【问题描述】:

请参考以下可运行的演示代码。它正在尝试将sr.str.extract() "a" 列插入多个列,并将这些列插入到 "a" 列之后的原始 df 立即
请以更好的方式将以下代码中的步骤[1][2]结合起来。

import re
import pandas as pd

df = pd.DataFrame({
     "a" : {1: 'a', 2: 'aa', 3: 'aaa'} ,
     "b" : {1: 'b', 2: 'bb', 3: 'bbb'} ,
     "c" : {1: 'b', 2: 'bb', 3: 'bbb'} ,
})

"""
df==
+----+-----+-----+-----+
|    | a   | b   | c   |
|----+-----+-----+-----|
|  1 | a   | b   | b   |
|  2 | aa  | bb  | bb  |
|  3 | aaa | bbb | bbb |
+----+-----+-----+-----+
"""
# step [1] sr.str.extract
rex = re.compile(r'(?P<firstletter>\w) (?P<secondletter>\w+)', re.X)
cols = df['a'].str.extract(rex)

# step [2] insert extracted columns back into the original df
df['firstletter'], df['secondletter'] = 0, 0
df['firstletter'] = cols['firstletter']
df['secondletter'] = cols['secondletter']
df = df['a firstletter secondletter b c'.split()]

"""
# Or, a more concise step [2], but too hard to glance thru and remember, also prone to mistake:
for col in cols.columns[::-1]:
    df.insert(df.columns.get_loc('a')+1, col, cols[col])
"""

# result:
"""
df==
+----+-----+---------------+----------------+-----+-----+
|    | a   | firstletter   | secondletter   | b   | c   |
|----+-----+---------------+----------------+-----+-----|
|  1 | a   | nan           | nan            | b   | b   |
|  2 | aa  | a             | a              | bb  | bb  |
|  3 | aaa | a             | aa             | bbb | bbb |
+----+-----+---------------+----------------+-----+-----+
"""

【问题讨论】:

  • 我希望inplace 操作看起来像:df.str.extract(col='a', rex, inplace=True),它将默认插入位置在 col 'a' 之后。
  • 查看我编辑的解决方案,下面有 2 个选项。
  • 请注意,inplace 解决方案通常被认为是一种不好的做法,并且很有可能在未来的 Pandas 版本中被贬值。见this postthis article

标签: python regex pandas


【解决方案1】:

有两种可能的解决方案:

解决方案一:

此解决方案与您的理想解决方案最相似,同时建议您对提取列的命名进行一些细微(但有建设性)的更改。例如。而不是firstlettersecondletter,为了提取列a,我们用前缀a_将其命名为a_firstlettera_secondletter。那么我们可以使用如下语句:

import re

df = df.assign(**df['a'].str.extract(r'(?P<a_firstletter>\w) (?P<a_secondletter>\w+)', re.X)).sort_index(axis=1)

结果:

print(df)


     a a_firstletter a_secondletter    b    c
1    a           NaN            NaN    b    b
2   aa             a              a   bb   bb
3  aaa             a             aa  bbb  bbb

解决方案 2:

此解决方案您可以继续使用firstlettersecondletter 作为提取的列名。

您可以通过.iloc 将列分成两部分:左侧部分df_left 从第一列到列a 和右侧部分df_right 从列a 之后的列直到最后。然后将左侧部分df_left、新提取的列cols和右侧部分df_right沿列通过pd.concat()连接在一起,如下所示:

df_left = df.iloc[:, 0: df.columns.get_loc('a')+1]
df_right = df.iloc[:, df.columns.get_loc('a')+1:]

df = pd.concat([df_left, cols, df_right], axis=1)

结果:

print(df)


     a firstletter secondletter    b    c
1    a         NaN          NaN    b    b
2   aa           a            a   bb   bb
3  aaa           a           aa  bbb  bbb

【讨论】:

  • 解决方案 1 的语法对我来说是全新的,非常好。我想重新排列列总是需要一些额外的努力。
  • @eliu 是的,使用df.assign(**df...)** 来解压色谱柱非常棘手。此外,此处重新排列的列是由.sort_index() 将提取的 2 个列与要提取的列一起重新排序。
  • 在现实世界中无法真正使用.sort_index(),其他列将重新排列。但是assign 真的很不错
  • @eliu 由于您已经很好地使用了Named Capturing Group 的正则表达式,因此.str.extract() 提取的列已经带有列名,您不需要显式编码2个列名在随后的代码中再次出现。
  • @eliu 如果您不能使用.sort_index() 对列名进行排序,那么您可能必须使用我的第二个解决方案。由于您需要提取列的特定位置,如果您不想使用.insert(),恐怕这将是最后的手段
【解决方案2】:

我相信你需要。

import re

df = pd.DataFrame({
     "a" : {1: 'a', 2: 'aa', 3: 'aaa'} ,
     "b" : {1: 'b', 2: 'bb', 3: 'bbb'} ,
     "c" : {1: 'b', 2: 'bb', 3: 'bbb'} ,
})

df[['firstletter','secondletter']] = df['a'].str.extract(r"(\w)(\w+)", expand=True)
print(df)

输出:

     a    b    c firstletter secondletter
1    a    b    b         NaN          NaN
2   aa   bb   bb           a            a
3  aaa  bbb  bbb           a           aa

【讨论】:

  • 我确实解决了在正则表达式和代码中输入 'firstletter' 'secondletter' 两次的问题。但将列放在所需位置仍然有点短。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 2019-10-29
  • 2020-04-04
相关资源
最近更新 更多