【问题标题】:Iterate over groups of rows in Pandas遍历 Pandas 中的行组
【发布时间】:2015-08-09 00:50:00
【问题描述】:

总的来说,我是 pandas 和 python 的新手——感谢您提供的任何指导!

我有一个包含 4 列的 csv 文件。我正在尝试将前三列在所有行上都相同的行组合在一起(A 列第 1 行 = A 列第 2 行,B 列第 1 行 = B 列第 2 行,依此类推)

我的数据如下所示:

   phone_number  state   date         description
1  9991112222    NJ      2015-05-14   Condo
2  9991112222    NJ      2015-05-14   Condo sales call
3  9991112222    NJ      2015-05-14   Apartment rental
4  6668885555    CA      2015-05-06   Apartment
5  6668885555    CA      2015-05-06   Apartment rental
6  4443337777    NJ      2015-05-14   condo

因此,在此数据中,第 1、2 和 3 行将在一个组中,而第 4 和 5 行将在另一组中。第 6 行将不在具有 1、2 和 3 的组中,因为它具有不同的 phone_number。

然后,对于每一行,我想使用 Levenshtein 距离将描述列中的字符串与该组中的 其他描述 进行比较,并保留描述足够相似的行。

第 1 行的“Condo”将与第 2 行的“Condo sales call”和第 3 行的“Apartment rental”进行比较。它不会与第 6 行的“condo”进行比较。

最终,目标是剔除描述与同一组中的另一个描述不够相似的行。换一种说法,打印出描述至少与该组中的另一个(任何其他)描述有点相似的所有行。理想输出:

   phone_number  state   date         description
1  9991112222    NJ      2015-05-14   Condo
2  9991112222    NJ      2015-05-14   Condo sales call
4  6668885555    CA      2015-05-06   Apartment
5  6668885555    CA      2015-05-06   Apartment rental

第 6 行不打印,因为它从未在一个组中。第 3 行未打印,因为“公寓出租”与“公寓”或“公寓销售电话”不够相似

这是我到目前为止的代码。我不知道这是否是最好的方法。如果到目前为止我做得对,我不知道如何打印感兴趣的整行:

import Levenshtein
import itertools 
import pandas as pd

test_data = pd.DataFrame.from_csv('phone_state_etc_test.csv', index_col=None)

for pn in test_data['phone_number']:
    for dt in test_data['date']:
        for st in test_data['state']:
            for a, b in itertools.combinations(test_data[
                                                     (test_data['phone_number'] == pn) & 
                                                     (test_data['state'] == st) & 
                                                     (test_data['date'] == dt)
                                                    ]
                                                     ['description'], 2):
                if Levenshtein.ratio(a,b) > 0.35:
                    print pn, "|", dt, "|", st, "|" #description

这会打印出一堆重复的这些行:

9991112222 | NJ | 2015-05-14 |
6668885555 | CA | 2015-05-06 |

但是如果我在打印行的末尾添加描述,我会得到一个

SyntaxError: invalid syntax 

对如何打印整行有什么想法吗?无论是 pandas 数据框,还是其他格式,都无所谓 - 我只需要输出到 csv 即可。

【问题讨论】:

    标签: python loops pandas


    【解决方案1】:

    您为什么不使用pandas.groupby 选项来查找唯一组(基于电话号码、州和日期)。这样做可以让您分别处理所有 Description 值,并为它们做任何您想做的事情。

    例如,我将与上述列进行分组,并获取该组中 Description 列的唯一值 -

    In [49]: df.groupby(['phone_number','state','date']).apply(lambda v: v['description'].unique())
    Out[49]: 
    phone_number  state  date      
    4443337777    NJ     2015-05-14                                        [condo]
    6668885555    CA     2015-05-06                  [Apartment, Apartment-rental]
    9991112222    NJ     2015-05-14    [Condo, Condo-sales-call, Apartment-rental]
    dtype: object
    

    您可以使用apply 中的任何函数。更多示例在这里 - http://pandas.pydata.org/pandas-docs/stable/groupby.html

    【讨论】:

      【解决方案2】:

      我不完全确定如何最好地对pandas 中的所有值对进行计算-在这里我制作了一个矩阵,其中包含行和列的描述(因此矩阵的主对角线比较本身的描述),但它似乎并不完全地道:

      def find_similar_rows(group, threshold=0.35):
          sim_matrix = pd.DataFrame(index=group['description'], 
                                    columns=group['description'])
          for d1 in sim_matrix.index:
              for d2 in sim_matrix.columns:
                  # Leave diagonal entries as nan
                  if d1 != d2:
                      sim_matrix.loc[d1, d2] = Levenshtein.ratio(d1, d2)
      
          keep = sim_matrix.gt(threshold, axis='columns').any()
          # A bit of possibly unnecessary mucking around with the index
          #   here, could probably be cleaned up
          rows_to_keep = group.loc[keep[group['description']].tolist(), :]
          return rows_to_keep
      
      grouped = test_data.groupby('phone_number', group_keys=False)
      
      grouped.apply(find_similar_rows)
      Out[64]: 
         phone_number state        date       description
      4    6668885555    CA  2015-05-06         Apartment
      5    6668885555    CA  2015-05-06  Apartment rental
      1    9991112222    NJ  2015-05-14             Condo
      2    9991112222    NJ  2015-05-14  Condo sales call
      

      【讨论】:

        【解决方案3】:

        从提供的数据看来,您希望保留描述中的第一个单词与该组最常见的第一个单词匹配的行。 如果是这种情况,您可以这样做:

        test_data['description_root'] = test_data.str.split().str[0] 
        # this adds a columns with the first word from the description column
        
        grouped = test_data.groupby(['phone_number', 'state', 'date'])
        most_frequent_root = grouped.description_root.transform(
                  lambda s: s.value_counts().idxmax())
        
        # this is a series with the same index as the original df containing 
        # the most frequently occuring root for each group
        
        test_data[test_data.description_root == most_frequent_root]
        # this will give you the matching rows
        

        您也可以在grouped 上致电.describe,为每个组提供一些额外信息。抱歉,如果这不是主题,但我认为您可能会发现 Series 字符串方法 (.str) 和 groupby 很有用。

        【讨论】:

        • 嗨,乔,感谢您的回答。我的目标实际上是比较描述下的整个字符串,而不是选择第一个单词。然而,回顾我的数据,我可以理解你从哪里得到的——很抱歉造成混乱。编辑:再次感谢,我会研究这些方法!
        • 没问题。我认为莱文斯坦距离可能过于复杂,但我知道很难提供关于 SO 复杂问题的简单示例。
        猜你喜欢
        • 2021-07-05
        • 2019-10-26
        • 2019-05-28
        • 2018-12-28
        • 2022-10-25
        • 1970-01-01
        • 2018-09-26
        • 2021-10-30
        相关资源
        最近更新 更多