通过正则表达式操作 Pandas 中的值

时间:2023-06-01 10:14:01 买帖  | 投诉/举报

技术标签:

【中文标题】通过正则表达式操作 Pandas 中的值【英文标题】:Manipulation of values in Pandas via Regex 【发布时间】:2017-01-11 21:46:18 【问题描述】:

这其实是here的后续问题。我之前的问题并不清楚,既然已经回答了,我觉得最好发布一个新问题。

我有一个如下的数据框:

Column1    Column2    Column3    Column4                     Column55FQ        1.047      S$55.3     UG44.2 as of 02/Jun/2016    S$8.2 mm600        (1.047)    S$23.3     AG5.6 as of 02/Jun/2016     S$58 mmKI2        1.695      S$5.35     RR59.5 as of 02/Jun/2016    S$705 mm88G        0.0025     S$(5.3)    NW44.2 as of 02/Jun/2016    S$112 mm60G        5.63       S$78.4     UG21.2 as of 02/Jun/2016    S$6.21 mm90F        (5.562)    S$(88.3)   IG46.2 as of 02/Jun/2016    S$8 mm

我正在尝试使用regex 删除所有单词和字母,只保留数字。但是,如果数字包含在 () 中,我想将其设为负数。

期望的输出

Column1    Column2    Column3    Column4       Column55          1.047      55.3       44.2          8.2600        -1.047     23.3       5.6           582          1.695      5.35       59.5          70588         0.0025     -5.3       44.2          11260         5.63       78.4       21.2          6.2190         -5.562     -88.3      46.2          8

这可能吗?我已经尝试过使用此代码,但不确定合适的 regex 组合应该是什么。

df.apply(lambda x: x.astype(str).str.extract(r"(\d+\.?\d*)", expand=True).astype(np.float))

【问题讨论】:

当我的描述性答案被否决时,我会写一条评论: (\d+.?\d*) 匹配所有具有任意小数位数的数字,包括日期的 02 和 2016。此外,您缺少标志。我首先将所有 "(" (反斜杠) 替换为 "-" 然后删除具有日期格式的所有内容然后删除 (替换为零字符串) 任何不是空格、数字或点的内容。类似 [^0-9 .]* (您需要查找它,因为正则表达式语法因环境而异。之后,您的结果由空格分隔,只需匹配 ((\d+.?\d*) ),结果在组间 【参考方案1】:
r1 = r"\((\d+\.?\d*)\)"r2 = r"(-?\d+\.?\d*)"df.stack().str.replace(r1, r"-\1", 1) \          .str.extract(r2, expand=False).unstack()

【讨论】:

非常感谢!只是想知道,我注意到如果值之间有逗号,例如$1,005A。它会丢弃除1 之外的所有内容。有没有办法让它保持1005【参考方案2】:

更新: $1,005A --> 1005(例如第一行,列Column3

In [131]: dfOut[131]:  Column1  Column2   Column3                   Column4    Column50     5FQ    1.047   $1,005A  UG44.2 as of 02/Jun/2016   S$8.2 mm1     600  (1.047)    S$23.3   AG5.6 as of 02/Jun/2016    S$58 mm2     KI2    1.695    S$5.35  RR59.5 as of 02/Jun/2016   S$705 mm3     88G   0.0025   S$(5.3)  NW44.2 as of 02/Jun/2016   S$112 mm4     60G     5.63    S$78.4  UG21.2 as of 02/Jun/2016  S$6.21 mm5     90F  (5.562)  S$(88.3)  IG46.2 as of 02/Jun/2016     S$8 mmIn [132]: to_replace = [r"\(([\d\.]+)\)", r".*?([\d\.\,\-]+).*", ","]In [133]: vals = [r"-\1", r"\1", ""]In [134]: df.replace(to_replace=to_replace, value=vals, regex=True)Out[134]:  Column1 Column2 Column3 Column4 Column50       5   1.047    1005    44.2     8.21     600  -1.047    23.3     5.6      582       2   1.695    5.35    59.5     7053      88  0.0025    -5.3    44.2     1124      60    5.63    78.4    21.2    6.215      90  -5.562   -88.3    46.2       8

旧答案:

另一个解决方案,它只使用DataFrame.replace() 方法:

In [28]: to_replace = [r"\(([\d\.]+)\)", r".*?([\d\.-]+).*"]In [29]: vals = [r"-\1", r"\1"]In [30]: df.replace(to_replace=to_replace, value=vals, regex=True)Out[30]:  Column1 Column2 Column3 Column4 Column50       5   1.047    55.3    44.2     8.21     600  -1.047    23.3     5.6      582       2   1.695    5.35    59.5     7053      88  0.0025    -5.3    44.2     1124      60    5.63    78.4    21.2    6.215      90  -5.562   -88.3    46.2       8

【讨论】:

感谢 MaxU。也在纳闷。如果columns 在值中有逗号,例如:$1,005A,则此代码将删除所有内容并保留值1。有没有办法修改代码使其只显示1005【参考方案3】:

你可以想出:

import redef onlynumbers(value):    if value.startswith("("):        return "-" + value    rx = re.compile(r"\d+[\d.]*")    try:        return rx.search(value).group(0)    except:        return valuedf.applymap(onlynumbers)

这会返回:

【讨论】:

你到底是怎么去掉日期的?您应该先消除那个,如下面我的回答所述。 @chrisvp:不,我不应该 - rx.search() 只返回第一个匹配不是日期的匹配项。 好的,但是第 5 列是 02,第 6 列是 2016,只有第 7 列是 8.2。因此,您需要跳过 5 和 6,归结为消除日期。 r"\d+[\d.]* " 可以短写为 r"[\d.]+" 不一定:让自己清楚[.\d]+(你的)、\d[.\d]*(我的)和更安全的\d[.\d]*\d之间的区别。有时缩短是为了不准确。

以上是关于通过正则表达式操作 Pandas 中的值的主要内容,如果未能解决你的问题,请参考以下文章