正则表达式策略在一种情况下有效,但在另一种情况下无效

时间:2019-08-13 14:12:47

标签: python regex pandas

在Python 3.7.3和Pandas 0.25.0上获得此数据

import pandas as pd
test = {'data':['1/2 lorem ipsum','2/3 ipsum lorem 4/5','6/7 lorem ipsum',
'8.2/9 ipsum lorem 10.12/13']}
df = pd.DataFrame(test)

我想分别提取分子和分母,并且只考虑最后一个分数,所以只有一个是给定的,如果第二个是两个,则为第二个。不得超过两个,并且它们之间是一些文本。

我将其用于分母,在前划线后提取任何数字:

print(df.data.str.extract('(?:.*\/(\d+)){0}.*\/(\d+)')[1])
0    2 
1    5 
2    7 
3    13
Name: 1, dtype: object

我无法使它适用于分子,可能是因为它们可能包含小数的复杂性。 我得到的最接近的是这种代码,使用的代码类似于上面的代码,加上对可能的小数的处理:

df.data.str.extract('(?:((?:\d+\.)?\d+)\/){0}(?:((?:\d+\.)?\d+)\/)')[1]
0      1
1      2
2      6
3    8.2
Name: 1, dtype: object

它正确地选择了小数点,但仅返回前几个分数的结果。预期的数字将是1,4,6,10.12

尝试了无数种代码变化后,我被卡住了,希望能找到错误。

3 个答案:

答案 0 :(得分:3)

我建议使用以下正则表达式:

includes

只要不在同一个字符串中的其他分数后面,它将匹配一个分数。

测试live on regex101.com

说明:

def index
  @schedule_dates = ScheduleDate.where(date: 1.week.ago..Float::INFINITY).includes(:orders)
end

答案 1 :(得分:1)

更多的熊猫样式代码,带有用于分数的简单正则表达式。

import pandas as pd
test = {'data':[
    '1/2 lorem ipsum',
    '2/3 ipsum lorem 4/5',
    '6/7 lorem ipsum',
    '8.2/9 ipsum lorem 10.12/13']}
df = pd.DataFrame(test)
fractions = df.data.str.extractall('(\d+\.?\d*)/(\d+)').groupby(level=0).tail(1)
numerators = fractions[0].tolist()
denominators = fractions[1].tolist()
print("Numerators:",numerators,"\nDenominators",denominators)

输出

Numerators: ['1', '4', '6', '10.12'] 
Denominators ['2', '5', '7', '13']

答案 2 :(得分:0)

您可以使用

>>> df.data.str.extract(r'(?:.*\D)?(?<!\d\.)(\d+(?:\.\d+)?)/(\d+(?:\.\d+)?)')
       0   1
0      1   2
1      4   5
2      6   7
3  10.12  13

请参见regex demo

详细信息

  • (?:.*\D)?-可选的字符串,除了换行符以外,最多0个字符,最多至非数字...
  • (?<!\d\.)-不是紧跟数字和点...
  • (\d+(?:\.\d+)?)-捕获第1组:1个以上的数字以及.和1个以上的数字的可选序列
  • /-一个/
  • (\d+(?:\.\d+)?)-捕获第2组:1个以上的数字以及.和1个以上的数字的可选序列。

如果您需要分别获取字符串 中最后一个分数的值,请删除不必要的分组:

>>> df.data.str.extract(r'(?:.*\D)?(?<!\d\.)(\d+(?:\.\d+)?)/\d+(?:\.\d+)?')
       0
0      1
1      4
2      6
3  10.12
>>> df.data.str.extract(r'(?:.*\D)?(?<!\d\.)\d+(?:\.\d+)?/(\d+(?:\.\d+)?)')
    0
0   2
1   5
2   7
3  13