【问题标题】:Google Sheets API and Pandas. Inconsistent data length from the API谷歌表格 API 和熊猫。来自 API 的数据长度不一致
【发布时间】:2020-06-18 18:43:32
【问题描述】:

我正在使用 google sheet API 获取数据,然后将其传递给 Pandas,以便我可以轻松地处理数据。

假设我想获得一张包含以下数据的工作表(描述为 JSON 对象,因为这里没有很好地展示表格)

{
  columns: ['Name', 'Age', 'Tlf.' 'Address'],
  data: ['Julie', '35', '12345', '8 Leafy Street']
}

Sheets API 将返回类似以下内容的内容:

{
  'range': 'Cases!A1:AE999',
   'majorDimension': 'ROWS',
    'values': 
    [
            ['Name', 'Age', 'Tlf.', 'Address'],
      ['Julie', '35', '12345', '8 Leafy Street']
    ]
}

这很棒,让我可以轻松地将列标题和数据传递给 Pandas,而不用大惊小怪。我通过以下方式执行此操作:

values = sheets_api_result["values"]
df = pd.DataFrame(values[1:], columns=values[0])
我的问题

如果我有一个如下表所示的 Gsuite 工作表,描述为键:值数据类型

{
  columns: ['Name', 'Age', 'Tlf.' 'Address'],
  data: ['Julie', '35', '', '']
}

我会收到以下回复

{
  'range': 'Cases!A1:AE999',
   'majorDimension': 'ROWS',
    'values': 
    [
            ['Name', 'Age', 'Tlf.', 'Address'],
      ['Julie', '35']
    ]
}

请注意,两个数组的长度不相等,并且返回的不是Nonenull 值,而是响应中根本不存在数据。

在我的代码中处理这些数据时,我最终会遇到如下所示的错误

ValueError: 4 columns passed, passed data had 2 columns

据我所知,我有两个选择:

  1. 想出一个聪明的方法在必要时用None填充我的回复
  2. 如果可能,指示 API 在存在空值的 JSON 中返回空值,尤其是当最后一列根本没有数据时。

关于第 1 点。我想我可以将 x None 值附加到 x 等于 length_of_column_heading_array - length_of_data_array 的列表中。然而,这看起来很丑陋,也许有一种更优雅的方法。

关于第 2 点,我还没有找到对我有帮助的答案。

如果有人对我如何解决这个问题有任何想法,我将不胜感激。

干杯!

【问题讨论】:

    标签: python python-3.x pandas google-sheets google-sheets-api


    【解决方案1】:

    如果有人感兴趣,这是我解决问题的方法。

    首先,我们需要从 Sheets API 中获取所有数据。

    # define the names of the tabs I want to get
    ranges = ['tab1', 'tab2']
    
    # Call the Sheets API
    request = service.spreadsheets().values().batchGet(spreadsheetId=document, ranges=ranges,)
    response = request.execute()
    

    现在我想遍历每一列并确保每一行的列表包含与包含列标题的第一行相同数量的元素。

    # response is the response from google sheets API, 
    # and from the code above. It contains column headings
    # and data from every row.
    
    # valueRanges is the key to access the data.
    def extract_case_data(response, keyword):
        for obj in response["valueRanges"]:
            if keyword in obj["range"]:
                values = pad_data(obj["values"])
                df = pd.DataFrame(values[1:], columns=values[0])
                return df
        return None
    

    最后,填充数据的方法

    def pad_data(data: list):
    
        # build a new array with the column heading data
        # this is the list which we will return
        return_data = [data[0]]
    
        for row in data[1:]:
            difference = len(data[0]) - len(row)
            new_row = row
            # append None to the lists which have a shorter
            # length than the column heading list
            for count in range(1, difference + 1):
                new_row.append(None)
            return_data.append(new_row)
        return return_data
    

    我当然不是说这是最好或最优雅的解决方案,但它已经为我解决了问题。

    希望这对某人有所帮助。

    【讨论】:

      【解决方案2】:

      同样的想法,也许看起来更简单:

      获取原始值

      result = service.spreadsheets().values().get(spreadsheetId=spreadsheet_id, range=data_range).execute()
      raw_values = result.get('values', [])
      

      然后在迭代中完成

      for row in raw_values:
          row = row + [''] * (expected_length - len(row))
      

      【讨论】:

        猜你喜欢
        • 2021-08-10
        • 2014-07-11
        • 2019-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-11
        • 2017-11-28
        • 1970-01-01
        相关资源
        最近更新 更多