从 SmartSheet API 创建 Pandas DataFrame(嵌套,尴尬,JSON)

Creating Pandas DataFrame from SmartSheet API (nested, awkward, JSON)

我正在尝试通过 Python 连接到我办公室的 SmartSheet API,以创建一些使用 SmartSheet 外部数据的绩效跟踪仪表板。我想要做的就是创建一个简单的 DataFrame,其中字段反映 columnId,单元格值反映 Smartsheet 字典中的 displayValue 键。我使用标准 API requests.get 而不是 SmartSheet 的 API 文档来执行此操作,因为我发现后者不太容易使用。

table(示例)设置为:

Number  Letter  Name
1       A       Joe
2       B       Jim
3       C       Jon

sheet GET 请求的 JSON 语法是:

{'id': 339338304219012,
 'name': 'Sample Smartsheet',
 'version': 1,
 'totalRowCount': 3,
 'accessLevel': 'OWNER',
 'effectiveAttachmentOptions': ['GOOGLE_DRIVE',
  'EVERNOTE',
  'DROPBOX',
  'ONEDRIVE',
  'LINK',
  'FILE',
  'BOX_COM',
  'EGNYTE'],
 'ganttEnabled': False,
 'dependenciesEnabled': False,
 'resourceManagementEnabled': False,
 'cellImageUploadEnabled': True,
 'userSettings': {'criticalPathEnabled': False, 'displaySummaryTasks': True},
 'userPermissions': {'summaryPermissions': 'ADMIN'},
 'hasSummaryFields': False,
 'permalink': 'https://app.smartsheet.com/sheets/5vxMCJQhMV7VFFPMVfJgg2hX79rj3fXgVGG8fp61',
 'createdAt': '2020-02-13T16:32:02Z',
 'modifiedAt': '2020-02-14T13:15:18Z',
 'isMultiPicklistEnabled': True,
 'columns': [{'id': 6273865019090820,
   'version': 0,
   'index': 0,
   'title': 'Number',
   'type': 'TEXT_NUMBER',
   'primary': True,
   'validation': False,
   'width': 150},
  {'id': 4022065205405572,
   'version': 0,
   'index': 1,
   'title': 'Letter',
   'type': 'TEXT_NUMBER',
   'validation': False,
   'width': 150},
  {'id': 8525664832776068,
   'version': 0,
   'index': 2,
   'title': 'Name',
   'type': 'TEXT_NUMBER',
   'validation': False,
   'width': 150}],
 'rows': [{'id': 8660990817003396,
   'rowNumber': 1,
   'expanded': True,
   'createdAt': '2020-02-14T13:15:18Z',
   'modifiedAt': '2020-02-14T13:15:18Z',
   'cells': [{'columnId': 6273865019090820, 'value': 1.0, 'displayValue': '1'},
    {'columnId': 4022065205405572, 'value': 'A', 'displayValue': 'A'},
    {'columnId': 8525664832776068, 'value': 'Joe', 'displayValue': 'Joe'}]},
  {'id': 498216492394372,
   'rowNumber': 2,
   'siblingId': 8660990817003396,
   'expanded': True,
   'createdAt': '2020-02-14T13:15:18Z',
   'modifiedAt': '2020-02-14T13:15:18Z',
   'cells': [{'columnId': 6273865019090820, 'value': 2.0, 'displayValue': '2'},
    {'columnId': 4022065205405572, 'value': 'B', 'displayValue': 'B'},
    {'columnId': 8525664832776068, 'value': 'Jim', 'displayValue': 'Jim'}]},
  {'id': 5001816119764868,
   'rowNumber': 3,
   'siblingId': 498216492394372,
   'expanded': True,
   'createdAt': '2020-02-14T13:15:18Z',
   'modifiedAt': '2020-02-14T13:15:18Z',
   'cells': [{'columnId': 6273865019090820, 'value': 3.0, 'displayValue': '3'},
    {'columnId': 4022065205405572, 'value': 'C', 'displayValue': 'C'},
    {'columnId': 8525664832776068, 'value': 'Jon', 'displayValue': 'Jon'}]}]}

以下是我解决问题的两种方法:

输入:

from pandas.io.json import json_normalize
samplej = sample.json()
s_rows = json_normalize(data=samplej['rows'], record_path='cells', meta=['id', 'rowNumber'])
s_rows

输出:

以 columnId、value、disdlayValue、id 和 rowNumber 作为自己的字段的 DataFrame。

如果我能弄清楚如何以正确的方式转置这些数据,我可能会让它工作,但这似乎非常复杂。

输入:

samplej = sample.json()
cellist = []
def get_cells():
    srows = samplej['rows']
    for s_cells in srows:
        scells = s_cells['cells']
        cellist.append(scells)
get_cells()
celldf = pd.DataFrame(cellist)
celldf

输出:

这是一个 returns 具有正确列数和行数的 DataFrame,但每个单元格都填充了一个看起来像

的字典
In [14]:
celldf.loc[1,1]
Out [14]:
{'columnId': 4022065205405572, 'value': 'B', 'displayValue': 'B'}

如果有办法删除除每个单元格中 displayValue 键对应的值以外的所有内容,这可能会解决我的问题。不过,这又一次显得异常复杂。

我对 Python 和 API 的工作还很陌生,所以可能有一种简单的方法可以解决我忽略的问题。或者,如果您有关于接近我上面概述的可能解决方案的建议,我会洗耳恭听。感谢您的帮助!

您必须使用 columns 字段:

colnames = {x['id']: x['title'] for x in samplej['columns']}
columns = [x['title'] for x in samplej['columns']]
cellist = [{colnames[scells['columnId']]: scells['displayValue']
            for scells in s_cells['cells']} for s_cells in samplej['rows']]
celldf = pd.DataFrame(cellist, columns=columns)

这符合预期:

  Number Letter Name
0      1      A  Joe
1      2      B  Jim
2      3      C  Jon

如果某些单元格只能包含 columnId 而没有 displayValue 字段,则应将上面代码中的 scells['displayValue'] 替换为 scells.get('displayValue', defaultValue),其中 defaultValue 可以是 None, np.nan 或任何其他相关默认值。