【问题标题】:Finding equality between different strings that should be equal在应该相等的不同字符串之间找到相等
【发布时间】:2014-03-19 15:31:53
【问题描述】:

我有来自三个不同来源的足球队数据。但是,来自这些来源的同一团队的“团队名称”在风格上有所不同。 例如

[Source1]             [Source2]  [Source3]
Arsenal               ARS        Arsenal
Manchester United     MNU        ManUtd
West Bromwich Albion  WBA        WestBrom

现在我经常需要比较这些团队名称(来自不同或相同的来源),以检查它们是相同还是不同的团队。例如

Arsenal == ARS  : True
MNU == WBA      : False
WBA == WestBrom : True

我想知道是否有一种简洁的 Python 方式来实现这一点。

我的想法如下: 创建一个类 Team,它有一个包含 3 个匹配团队名称的元组的元组列表。实例化一个。每个团队名称的 Team 对象。然后覆盖该类的__eq__ 方法,我将在其中对元组列表执行reduce,以查找所讨论的两个团队名称是否属于同一个表示相等的元组。

一些伪代码:

class Team:
  def __init__(self, teamname):
    self.teams = [(Arsenal, ARS, Arsenal),
                  (Manchester United, MNU, ManUtd),
                  (West Bromwich Albion, WBA, WestBrom),]
    self.teamname = teamname

  def __eq__(self, teamA, teamB):
    reduce(self.teams, <check if teamA and teamB in same tuple>)

想法?

P.S.:请为这个问题提出一个更好的Title,因为我认为我在这方面做得不好。

编辑:扩展我建议的解决方案

【问题讨论】:

    标签: python design-patterns equality


    【解决方案1】:

    为简单起见,您可以将所有内容放在一个平面规范查找中:

    canonical = {'Arsenal':'ARS',
                 'ARS':'ARS',
                 'Manchester United':'MNU',
                 'MNU':'MNU',
                 'ManUtd':'MNU',
                 ...}
    

    那么等价测试就很简单了:

    if canonical[x] == canonical[y]:
        #they're the same team
    

    这里有很多不错的替代答案,如此广泛:如果您从不期望您的 canonical 查找发生变化,这种方法很好。您可以生成一次然后忘记它。如果它确实经常改变,那么维护起来会很痛苦,所以你应该去别处看看。

    【讨论】:

    • 我写了一段代码,它反转了canonical 并使它更容易维护这种方式。代码在我下面的答案中
    • 谢谢 roippi。我喜欢这个解决方案的简单性。 jonrsharpe 的课程路线最适合我的用例。
    【解决方案2】:

    你可以有某种等价映射:

    equivalents = {"Arsenal": ["ARS",], 
                   "Manchester United": ["MNU", "ManUtd"], ...}
    

    并使用它来处理您的数据:

    >>> name = "ManUtd"
    >>> for main, equivs in equivalents.items():
        if name == main or name in equivs:
            name = main
            break
    
    >>> name 
    "Manchester United"
    

    这使您可以轻松查看您认为是团队的“规范名称”(即键)和其他被视为同一团队的名称(即列表值)。


    如果你确实走类路线,你应该将团队元组列表设为类属性:

    class Team:
    
        TEAMS = [("Arsenal", "ARS"), ("Manchester United", "MNU", "ManUtd"), ...]
    
        def __init__(self, name):
            if not any(name in names for names in self.TEAMS):
                raise ValueError("Not a valid team name.")
            self.name = name
    
        def __eq__(self, other):
            for names in self.TEAMS:
                if self.name in names and other.name in names:
                    return True
            return False
    

    这个输出:

    >>> mnu1 = Team("ManUtd")
    >>> mnu2 = Team("MNU")
    >>> mnu1 == mnu2
    True
    
    >>> ars = Team("ARS")
    >>> ars == mnu1
    False
    
    >>> fail = Team("Not a name")
    Traceback (most recent call last):
      File "<pyshell#49>", line 1, in <module>
        fail = Team("Not a name")
      File "<pyshell#43>", line 7, in __init__
        raise ValueError("Not a valid team name.")
    ValueError: Not a valid team name.
    

    或者,如果您的 Team 没有其他属性,则只需一个简单的函数即可完成相同的工作:

    def equivalent(team1, team2):
        teams = [("Arsenal", "ARS"), ("Manchester United", "MNU", "ManUtd"), ...]
        for names in teams:
            if team1 in names and team2 in names:
                return True
        return False
    

    输出:

    >>> equivalent("MNU", "ManUtd")
    True
    >>> equivalent("MNU", "Arsenal")
    False
    >>> equivalent("MNU", "Not a name")
    False
    

    【讨论】:

    • 类路由是最适合我的用例的。谢谢!
    【解决方案3】:

    如果你定义一个反转字典的函数,roippi的代码可以更好地维护:

    def invertdict(d):
      id=dict()
      for (key,value) in d.items():
        for part in value:
          if part in id:
            id[part]=id[part]+(key,)
          else:
              id[part]=(key,)
      return id
    

    如果这样做,canonical 的值必须定义为元组:

    canonical = {'Arsenal':('ARS',),
                 'ARS':('ARS',),
                 'Manchester United':('MNU',),
                 'MNU':('MNU',),
                 'ManUtd':('MNU',)}
    

    但是你可以简单地:

    print invertdict(canonical)
    {'ARS': ('ARS', 'Arsenal'), 'MNU': ('ManUtd', 'Manchester United', 'MNU')}
    print invertdict(invertdict(canonical))
    {'MNU': ('MNU',), 'Manchester United': ('MNU',), 'ARS': ('ARS',), 'Arsenal': ('ARS',), 'ManUtd': ('MNU',)}
    # this is canonical again
    

    那么您可能想在开始时定义倒置规范并使用invertdict 得到canonical 并能够比较您的团队

    希望对你有帮助

    【讨论】:

      【解决方案4】:

      我会做什么:

      class Team:
          def __init__(self, name, all_names):
              self.name = name  # use name as it's "proper" name
              self.all_names = all_names # use a list of all acceptable names and abbreviaitons
      
      man = Team('Manchester United',['Manchester United', 'MNU', 'ManUtd'])
      

      然后你可以使用if 'MNU' in man.all_names

      【讨论】:

        【解决方案5】:

        我认为最好的方法是接近你所拥有的,使用所有相关名称的元组列表。

        def __eq__(self, teamA, teamB):
            for names in self.teams:
                if teamA in names:  break
        
            if (teamA and teamB) in names: #Must include teamA in this comparison to avoid false positive from last entry of self.teams containing teamB but not teamA
                 return True
            else:
                 return False
        

        这比使用字典或缩写列表具有优势,因为使用哪个名称版本作为“键”并不重要


        您可以尝试使用以下方式自动匹配:

        def __eq__(self, teamA, teamB):
            if len(teamA) > len(teamB):
                return all([l in teamA.lower() for l in teamB.lower()])
            elif len(teamA) < len(teamB):
                return all([l in teamB.lower() for l in teamA.lower()])
            else:
                return teamA.lower() == teamB.lower()
        

        请注意,此方法并不完美,因为它要求缩写的所有字母都在完整版本中(可能并非总是如此)。你可以建立一个比我这里更复杂的匹配方案,这样会得到更可靠的结果

        【讨论】:

          猜你喜欢
          • 2014-07-19
          • 2014-03-06
          • 2014-07-15
          • 2015-09-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-19
          • 1970-01-01
          相关资源
          最近更新 更多