Python - 匹配和解析包含 numeric/currency 金额的字符串

Python - match and parse strings containing numeric/currency amounts

假设我在 python 中有以下字符串(输入):

1) "$ 1,350,000" 2) "1.35 MM $" 3) "$ 1.35 M" 4)1350000(现在是数值)

虽然字符串表示形式不同,但显然数字是相同的。我怎样才能实现字符串匹配,或者换句话说,将它们归类为相等的字符串?

一种方法是使用正则表达式对可能的模式进行建模。不过可能有一种情况是我没有想到的。

有人看到这个问题的 NLP 解决方案吗?

谢谢

这不是 NLP 问题,只是正则表达式的工作,加上一些忽略顺序的代码,并查找已知缩写的字典 (/ontology),例如 "MM".

  • 首先,您可以完全忽略此处的“$”字符(除非您需要消除与其他货币或符号的歧义)。
  • 所以这一切归结为解析数字格式,并映射 'M'/'MM'/'million' -> 1e6 乘数。并以与顺序无关的方式进行解析(例如,乘数、货币符号和金额可以以任何相对顺序出现,或者根本不出现)

这是一些工作代码:

def parse_numeric_string(s):

    if isinstance(s, int): s = str(s)

    amount = None
    currency = ''
    multiplier = 1.0

    for token in s.split(' '):

        token = token.lower()

        if token in ['$','€','£','¥']:
            currency = token

        # Extract multipliers from their string names/abbrevs
        if token in ['million','m','mm']:
            multiplier = 1e6
        # ... or you could use a dict:
        # multiplier = {'million': 1e6, 'm': 1e6...}.get(token, 1.0)

        # Assume anything else is some string format of number/int/float/scientific
        try:
            token = token.replace(',', '')
            amount = float(token)
        except:
            pass # Process your parse failures...

    # Return a tuple, or whatever you prefer
    return (currency, amount * multiplier)

parse_numeric_string("$ 1,350,000")
parse_numeric_string("1.35 MM $")
parse_numeric_string("$ 1.35 M")
parse_numeric_string(1350000)
  • 对于国际化,您可能需要注意 ,. 作为千位分隔符和小数点可以切换,或者 ' 作为(阿拉伯语)千位分隔符。还有一个第三方 Python 包 'parse',例如parse.parse('{fn}', '1,350,000')(与format()相反)
  • 使用 ontology 或通用 NLP 库可能会比它的价值更麻烦。例如,您需要在 "accounting abbreviation for millions" 中的 'mm' 与 "millimeters" 和 'Megameters, 10^6 meters' 中的 'Mm' 之间消除歧义,后者几乎从未使用过但距离的有效公制单位。因此,较少的通用性可能更适合这项任务。
  • 您还可以使用基于字典的方法来映射其他货币符号,例如'dollars','US','USD','US$','EU'...
  • 这里我在空格上进行了标记,但您可能希望在任何 word/numeric/whitespace/punctuation 边界上进行标记,以便您可以解析例如USD1.3m

考虑创建一个例程,将字符串输入(可以是任何 4 种给定格式)与 4 种正则表达式模式相匹配,如下所示:

对于“1,350,000 美元”:

(?<=$ )([\d,]+)

对于“1.35 MM $”:

([\d\.]+)(?= MM $)

对于“135 万美元”:

(?<=$ )([\d\.]+)(?= M)

对于 1350000:

([\d]+)

然后将这些匹配转换成整数返回并与其他匹配。

提供的正则表达式模式将仅匹配字符串的数字逗号和小数位(通过使用先行和后行)。

注意: 根据匹配的正则表达式,将要求对提取的数字进行相应处理。 (例如,“$ 1.35 M”中的 1.35 需要乘以 1000000 才能返回)

有趣的问题,这是我的解决方案。
你可以写一些 class 来寻找潜在的匹配项,将它们分隔成一个数量和一个单位,然后尝试转换它们:

import re, locale, math

# us
locale.setlocale(locale.LC_ALL, 'en_US')
from locale import atof

data = """
Say I have the following strings (inputs) in python:

1) "$ 1,350,000" 2) "1.35 MM $" 3) "$ 1.35 M" 4) 1350000 (now it is a numeric value)

Obviously the number is the same although the string representation is different. How can I achieve a string matching or in other words classify them as equal strings?

One way would be to model -using regular expressions- the possible patterns. However there might be a case that I haven't thought of.

Does someone see a NLP solution to this problem?
Thanks

here might be some other digits: 1.234
"""

class DigitMiner:
    def __init__(self):
        self.numbers = []

    def convert(self, amount, unit):
        if unit in ['M', 'MM']:
            amount *= 10**6
        elif unit in ['K']:
            amount *= 10**3
        else:
            pass
        return amount

    def search(self, string=None):
        rx = re.compile(r'''
            (?P<amount>\b\d[\d.,]+\b)\s*
            (?P<unit>M*)''', re.VERBOSE)

        for match in rx.finditer(string):
            amount = self.convert(atof(match.group('amount')), match.group('unit'))
            if amount not in self.numbers:
                self.numbers.append(amount)


dm = DigitMiner()
dm.search(data)
print(dm.numbers)

这产生:

[1350000.0, 1.234]


请注意,locale.atof() 将字符串转换为浮点数,遵循 LC_NUMERIC 设置。