【问题标题】:Python - Matching and extracting data from excel with pandasPython - 使用 pandas 从 excel 中匹配和提取数据
【发布时间】:2021-10-05 15:40:23
【问题描述】:

我正在编写一个 python 脚本,它可以自动为我拨打一些电话。我有一个可以与 REST API 交互的工具进行测试。我需要根据输入的国家代码选择特定的运营商。所以假设我的用户在我的 excel 文档中输入 12145221414,我想选择 AT&T 作为运营商。如何接受表格第一列的输入,然后输出第二列的内容?

显然,这可能有点棘手,因为我需要匹配电话号码前面最多 3-4 位数字。我的计划是编写一个函数,然后获取初始号码,然后插入需要用于该国家/地区的运营商。

知道如何从表中提取这些数据吗?如果您输入巴巴多斯 (1246),我将如何做到这一点,然后选择 Lime 而不是 AT&T?

这是我到目前为止的代码和表格。我不确定如何读取一个表,然后从该表中提取数据以用于我的匹配函数。

testlist.xlsx

| Number |
|:------------|
|8155555555|
|12465555555|
|12135555555|
|96655555555|
|525555555555|

carriers.xlsx

| countryCode | Carrier |
|:------------|:--------|
|1246|LIME|
|1|AT&T|
|81|Softbank|
|52|Telmex|
|966|Zain|
import pandas as pd
import os

FILE_PATH = "C:/temp/testlist.xlsx"
xl_1 = pd.ExcelFile(FILE_PATH)
num_df = xl_1.parse('Numbers')

FILE_PATH = "C:/temp/carriers.xlsx"
xl_2 = pd.ExcelFile(FILE_PATH)
car_df = xl_2.parse('Carriers')


for index, row in num_df.iterrows():

【问题讨论】:

  • 如果可能,请尝试使用 CSV。 CSV 是开源的,也与 excel 兼容。 XLSX 是专有的,支持有限。
  • 使用 CSV 的问题在于它没有像 XLS 或 XLSX 文件那样被压缩,从而导致文件呈指数级增长。更不用说在以 CSV 格式保存时处理大量数字的问题,就像在这个线程 stackoverflow.com/questions/22647042/… 中一样
  • 换句话说,您想从第二个表中找到您号码中包含的最长前缀吗?
  • 你能用你需要的正则表达式匹配来检查my answer吗?它应该相对较快,因为只使用第一个匹配项。

标签: python python-3.x pandas dataframe iterator


【解决方案1】:

让我们假设以下输入:

>>> df1
         Number
0    8155555555
1   12465555555
2   12135555555
3   96655555555
4  525555555555

>>> df2
   countryCode   Carrier
0         1246      LIME
1            1      AT&T
2           81  Softbank
3           52    Telmex
4          966      Zain

首先我们需要修改一点 df2 以降序对countryCode 进行排序,使其成为字符串并将其设置为索引。

后面的技巧是按降序对countryCode 进行排序。这将确保较长的国家/地区代码(例如“1246”)在较短的国家/地区代码(例如“1”)之前匹配。

>>> df2 = df2.sort_values(by='countryCode', ascending=False).astype(str).set_index('countryCode')
>>> df2
              Carrier
countryCode          
1246             LIME
966              Zain
81           Softbank
52             Telmex
1                AT&T

最后,我们使用由国家代码按降序排列的正则表达式(此处为'1246|966|81|52|1' 使用'|'.join(df2.index))来提取最长的代码,并将其映射到运营商:

(df1.astype(str)['Number']
    .str.extract('^(%s)'%'|'.join(df2.index))[0]
    .map(df2['Carrier'])
)

输出:

0    Softbank
1        LIME
2        AT&T
3        Zain
4      Telmex
Name: 0, dtype: object

注意。将其添加到初始数据框:

df1['carrier'] = (df1.astype(str)['Number']
                     .str.extract('^(%s)'%'|'.join(df2.index))[0]
                     .map(df2['Carrier'])
                 ).to_clipboard(0)

输出:

         Number   carrier
0    8155555555  Softbank
1   12465555555      LIME
2   12135555555      AT&T
3   96655555555      Zain
4  525555555555    Telmex

【讨论】:

  • 这个解决方案和下面 Niel 的解决方案的组合将帮助我完成这项工作!非常感谢!
  • 你想为社区提供这个组合吗? ;)
  • 我一定会的,Mozway。一旦我把它熨平..
【解决方案2】:

我只能想到一个低效的解决方案。

首先,将运营商的数据框按国家代码倒序排列。这样,较长的前缀将更接近开头。

codes = xl_2.sort_values('countryCode', ascending=False)

接下来,定义一个函数,将数字与第二个数据框中的每个国家/地区代码进行匹配,并找到第一个匹配项的索引(如果有)(请记住,匹配项是最长的)。

def cc2carrier(num):
    matches = codes['countryCode'].apply(lambda x: num.startswith(x))
    if not matches.any(): #Not found
        return np.nan
    return codes.loc[matches.idxmax()]['Carrier']

现在,将函数应用于数字数据框:

xl_1['Number'].apply(cc2carrier)
#1    Softbank
#2        LIME
#3        AT&T
#4        Zain
#5      Telmex
#Name: Number, dtype: object
    

【讨论】:

    【解决方案3】:

    如果我理解正确,您只想从输入列(数字)中获取第一个字符,然后将其与载体.xlsx 中的第二个数据帧匹配。

    提取数字列的第一个字符。提示:nbr_of_chars 变量应基于carrier.xlsx 中countryCode 列的最大字符长度

    nbr_of_chars = 4
    df.loc[df['Number'].notnull(), 'FirstCharsColumn'] = df['Number'].str[:nbr_of_chars]
    

    那么使用数据框连接进行匹配应该相当容易。

    【讨论】:

      【解决方案4】:

      知道如何从表中提取这些数据吗?我怎么会 使如果您输入巴巴多斯 (1246),则选择石灰 而不是 AT&T?

      carriers.xlsx

      countryCode Carrier
      1246 LIME
      1 AT&T
      81 Softbank
      52 Telmex
      966 Zain

      script.py

      import pandas as pd
      
      FILE_PATH = "./carriers.xlsx"
      df = pd.read_excel(FILE_PATH)
      rows_list = df.to_dict('records')
      code_carrier_map = {}
      
      for row in rows_list:
          code_carrier_map[row["countryCode"]] = row["Carrier"]
      
      print(type(code_carrier_map), code_carrier_map)
      
      print(f"{code_carrier_map.get(1)=}")
      print(f"{code_carrier_map.get(1246)=}")
      print(f"{code_carrier_map.get(52)=}")
      print(f"{code_carrier_map.get(81)=}")
      print(f"{code_carrier_map.get(966)=}")
      

      输出

      $ python3 script.py 
      <class 'dict'> {1246: 'LIME', 1: 'AT&T', 81: 'Softbank', 52: 'Telmex', 966: 'Zain'}
      code_carrier_map.get(1)='AT&T'
      code_carrier_map.get(1246)='LIME'
      code_carrier_map.get(52)='Telmex'
      code_carrier_map.get(81)='Softbank'
      code_carrier_map.get(966)='Zain'
      

      那么如果你想解析电话号码,不要重新发明轮子,只需使用这个phonenumbers 库即可。

      代码

      import phonenumbers
      
      num = "+12145221414"
      phone_number = phonenumbers.parse(num)
      print(f"{num=}")
      print(f"{phone_number.country_code=}")
      print(f"{code_carrier_map.get(phone_number.country_code)=}")
      

      输出

      num='+12145221414'
      phone_number.country_code=1
      code_carrier_map.get(phone_number.country_code)='AT&T'
      

      【讨论】:

      • 这非常有用!我主要构建了程序化的 selenium 脚本,我正试图打破我的外壳。非常感谢您的帮助!
      • 我很惊讶我不知道有电话号码模块。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-31
      • 2018-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-01
      相关资源
      最近更新 更多