如何从无序的API端点获取pandas DataFrame
作者选择将自由开源基金作为”为捐款而写”计划的一部分收到的捐款之一。
引言
与Excel电子表格或SQL表类似,pandas DataFrame是一个二维数据结构,数据以行和列的形式呈现。DataFrame主要用于数据密集型领域,如科学学习、数据科学、机器学习和数据分析。
使用Python,您可以将来自CSV、Excel或API endpoints的数据转换为pandas DataFrame,然后对获得的DataFrame进行操作。虽然将Excel和CSV文件转换成DataFrame往往很简单,但是API endpoints可能各不相同,因此从这些信息中创建DataFrame可能具有挑战性。
有些 API 端点以读者友好的字典格式存在,其中键表示列名,值表示属于这些列的元素。然而,有些 API 端点没有以清晰的上下文表示列名和它们的值,将无序的 API 端点转换为 DataFrame 可能会具有挑战性。
在这个教程中,您将从一个无序的端点获取数据,并以适当的格式呈现给 pandas DataFrame。首先,您将从一些传统的 API 格式中提取数据,以便将有序的 API 数据转化为 DataFrame。然后,您将使用一个无序的 API 端点。您将提取列名及其相应的元素,创建一个字典。最后,您将将这个字典传入 pandas DataFrame,以便进行数据分析。
先决条件
要跟随这个教程,您需要:
- Python 3, pip, and the Python venv module installed on your machine. For Ubuntu and other Linux distributions, follow Steps 1 and 2 of our tutorial on How To Install Python 3 and Set Up a Programming Environment. For macOS, follow Steps 1–5 of our tutorial on How To Install Python 3 and Set Up a Local Programming Environment on macOS.
- Jupyter notebook installed and running on your machine. For remote servers running Ubuntu/Debian, follow How to Install, Run, and Connect to Jupyter Notebook on a Remote Server. For other operating systems, check the official documentation for installing Jupyter.
- A modern web browser that you will use to access Jupyter Notebook.
- Familiarity with the fundamentals of Python, such as for loops, functions, and data structures (in particular, lists and dictionaries).
- Experience with the Python pandas module for data manipulation and analysis.
第一步 – 准备您的Jupyter Notebook环境
在本教程中,您将使用Jupyter Notebook来处理数据。Jupyter Notebook对于迭代式编码非常有帮助,因为它允许您编写一小段代码,运行它,并返回结果。
要在您的Jupyter Notebook中跟随本教程,您需要打开一个新的笔记本并安装所需的依赖项,在本步骤中将进行此操作。
在先决条件中,您在服务器上设置了一个Jupyter Notebook环境。一旦您登录服务器,激活虚拟环境。
- source ~/environments/my_env/bin/activate
然后运行Jupyter Notebook应用程序来启动应用程序。
- jupyter notebook
Note
打开一个新的终端并输入以下命令:
ssh -L 8888:localhost:8888 your_non_root_user@your_server_ip
连接服务器后,导航到输出提供的链接以访问您的Jupyter Notebook。在本教程的剩余部分保持此终端开放。
运行并连接到它后,您将访问基于浏览器的用户界面。从“新建”下拉菜单中,选择Python3(ipykernel)选项,将会打开一个新标签页,其中包含一个未命名的Python笔记本。给您的文件取一个名称;在本教程中,文件名为convert_data.ipynb。
然后,在浏览器的Jupyter笔记本的第一个单元格中,使用pip安装必要的依赖项。
pip install pandas
pip install requests
DataFrame数据结构只能通过pandas模块来访问。requests模块帮助您获取URL访问权限。
在将命令添加到您的Jupyter Notebook之后,按下运行按钮来执行它们。
你的Jupyter笔记本将会提供一个运行输出来指示依赖项正在下载。在此输出下方将会有一个新的输入单元格,你将在此运行下一行代码。
接下来,通过运行以下命令导入刚刚安装的模块。
import pandas as pd
import requests
pd将为您提供调用pandas模块的快捷方式。您只需要写下pd即可调用pandas模块。
在这一步骤中,您已经准备好了您的Jupyter Notebook环境。接下来,您将执行一些基本的转换,从字典创建数据帧。
第二步 — 将有序的API数据转换为pandas DataFrame。
在从无序的API端点获取数据之前,您将首先从不同格式的字典和URL上组织的API数据创建数据框架。
在许多API端点中,键通常表示列名,而这些键的值表示属于相应列的元素。然而,有些API端点不是以这种格式呈现,这意味着您需要一种方法来提取列名及其相应的数据,以创建一个可读的字典,从字典中可以创建一个pandas DataFrame。回顾一些将传统API端点转换为pandas DataFrame的常见方法将有助于您完成此任务。
在这一步骤中,您将使用一些常见的方法从不同格式的字典创建数据帧。
方法1 – 从字典列表创建数据框
在这种方法中,列表中的每个字典都代表最终DataFrame中的一行数据。DataFrame的结构将包含代表列名的键,以及属于该列的元素的值。
使用这种方法,您将创建一个具有年龄、ID和姓名列以及三个人的信息的DataFrame。
在一个新的单元格中,运行以下代码来格式化分配给变量dict1的字典列表(可以随意用您自己的名称替换)。
dict1=[{"Age":41,"ID":1234,"Name":"Bella"},{"Age":24,"ID":5678,"Name":"Mark"},{"Age":26,"ID":2341,"Name":"John"}]
data=pd.DataFrame(dict1)
data.head()
你将一个字典列表分配给dict1变量。列表中的每个字典表示结果DataFrame的一行。键Age,ID和Name代表列名。分配给每个键的值表示该行的该列的值。然后,你将该字典传递给pandas模块以创建DataFrame。
以下DataFrame将打印出来:
Age ID Name 0 41 1234 Bella 1 24 5678 Mark 2 26 2341 John
这个DataFrame是从字典列表创建的。dict1列表中每个字典中定义的键是获得的DataFrame中的列名,而这些字典中的值表示逐行的数据。
方法2——从单个字典创建数据框
在之前的方法中,你是根据行定义数据,这会导致在一个列表中输入多个字典。每个字典表示按行排列的数据,其中键是列名,值是属于该列的元素。
现在,你将以按列的方式定义数据。这一次,你将使用一个字典,其中键表示列名,值是代表该列垂直数据的列表。
使用这种方法,您可以使用在前面部分中使用过的相同数据。在一个新的单元格中运行以下行:
dict2={"Age":[41,24,26],"ID":[1234,5678,2341],"Name":["Bella","Mark","John"]}
data=pd.DataFrame(dict2)
data.head()
在这里,您使用一个字典来定义逐列的数据。dict2字典的键是列名:年龄,ID和姓名。每个键的值是一个列表,表示每列的数据。
下面的数据框将会打印到屏幕上。
Age ID Name 0 41 1234 Bella 1 24 5678 Mark 2 26 2341 John
dict2字典的键代表列名(年龄、ID和姓名),每列都填充了你定义的列表作为该键的值。例如,年龄列的值是41、24和26。
果不其然,两种方法产生了相同的结果。虽然前两种方法的输出相同,但你可以同时使用按行和按列的方法来创建一个数据框。
方法3 — 通过URL创建包含API数据的DataFrame
在这种方法中,您将使用从URL提取的API数据创建一个DataFrame。在与API相关的URL中,数据通常以字典列表的形式呈现,类似于方法一中使用的数据。但是,您不会硬编码数据,而是使用requests模块访问URL上的API数据,该模块可以帮助您访问URL。
对于这种方法,你将使用一个JSON API的URL:Countries with Capitals,它以API格式展示了一个国家和其首都的列表。要查看这个API,请在浏览器中访问以下URL。
- https://raw.githubusercontent.com/dariusk/corpora/master/data/geography/countries_with_capitals.json
这里是由URL提供的API数据的起始部分。
{
"description": "A list of countries and its respective capitals.",
"countries": [
{"name":"Afghanistan", "capital":"Kabul"},
{"name":"Albania", "capital":"Tirana"},
{"name":"Algeria", "capital":"Algiers"},
{"name":"Andorra", "capital":"Andorra la Vella"},
{"name":"Angola", "capital":"Luanda"},
{"name":"Antigua & Barbuda", "capital":"St. John's"},
{"name":"Argentina", "capital":"Buenos Aires"},
{"name":"Armenia", "capital":"Yerevan"},
{"name":"Australia", "capital":"Canberra"},
{"name":"Austria", "capital":"Vienna"},
...
API以一种可识别的格式进行展示:外部字典中的国家键包含一个字典列表,name和capital是列名,它们的对应值是按行排列的数据。
要从此URL创建一个pandas DataFrame,请在新单元格中运行以下代码。
response=requests.get('https://raw.githubusercontent.com/dariusk/corpora/master/data/geography/countries_with_capitals.json').json()
df1=pd.DataFrame(response['countries'])
df1.head()
requests模块允许您访问URL。在这种情况下,您将URL读取为JSON文件,并将读取的数据赋给变量response。接下来,您将这个字典列表(作为countries键的值提供)呈现给pandas,pandas再提供一个DataFrame。成功从给定API端点提取数据后,df1将是所获得的DataFrame。
运行上述代码后,你将会得到以下的DataFrame:
name capital 0 Afghanistan Kabul 1 Albania Tirana 2 Algeria Algiers 3 Andorra Andorra la Vella 4 Angola Luanda
这是在将国家和首都列表传递给pandas DataFrame后获得的数据表。当你运行这段代码时,你将字典列表(作为国家键的值)传递给了pandas DataFrame模块。列名为name和capital,因为它们在列表中的每个字典中都作为键存在,它们的值代表逐行的数据(因为每个字典代表一行)。
到目前为止,您已经从基本字典和结构化API端点创建了DataFrame。当处理包含结构化API的URL时,数据通常以方法1中使用的格式呈现:作为一个字典的列表,其中每个字典表示逐行的数据。在API数据是非结构化的情况下,您可以使用第1或第2种方法的格式按行或按列提取列名和它们的数据。在下一步中,您将结合来自此步骤的一些策略,从非结构化的API端点获取数据。
第三步 — 将无序的API数据转换为pandas DataFrame。
直到现在,您已经使用多种方法将有序数据转换为pandas Dataframe。现在,您将使用不同格式的数据进行处理。这个新URL中的API数据不是常规格式,因此很难确定列名和它们的数据。
您需要采取额外的步骤来从无序数据中创建DataFrame。首先,您需提取列名和所有列的值,然后将列名定义为字典中的键,将列元素(以列表形式)定义为字典中的值。最终,您将获得一个可展示的字典,类似于方法2所使用的格式,并将其提交给pandas以转换为DataFrame。
这个步骤使用来自纽约学校人口统计和问责快照的数据。
追踪API端点中的列名
在本节中,您将跟踪与API端点中包含的数据列相关的信息。
要访问样本数据,请使用您选择的网络浏览器打开此网址。
- https://data.cityofnewyork.us/api/views/ihfw-zy9j/rows.json
这个URL中的数据比你在第二步中处理的数据更不易展示。纽约快照数据呈现了各种形式的字典,但它们并不是以键代表列名,值代表列元素的字典。似乎没有以行或列方式呈现数据的简单字典。
您将通过查找与DataFrame相关的列信息来开始。在浏览器中访问API网址时,按下CTRL+F并搜索“columns:”。
...
"columns" : [ {
"id" : -1,
"name" : "sid",
"dataTypeName" : "meta_data",
"fieldName" : ":sid",
"position" : 0,
"renderTypeName" : "meta_data",
"format" : { },
"flags" : [ "hidden" ]
}, {
"id" : -1,
"name" : "id",
"dataTypeName" : "meta_data",
"fieldName" : ":id",
"position" : 0,
"renderTypeName" : "meta_data",
"format" : { },
"flags" : [ "hidden" ]
}, {
"id" : -1,
"name" : "position",
"dataTypeName" : "meta_data",
"fieldName" : ":position",
"position" : 0,
"renderTypeName" : "meta_data",
"format" : { },
"flags" : [ "hidden" ]
...
在列键的值列表中有几个字典。在每个字典中,注意dataTypeName键的值。对于前八个字典,dataTypeName键的值是元数据。元数据是描述数据的数据。在这种情况下,元数据不是表格内包含的数据,而是描述整个表格的数据。因此,具有dataTypeName键值为元数据的这八个字典不代表表格中存在的列的信息。
然而,当您继续阅读此列表时,请注意第一个dataTypeName键的出现,其值不是meta_data。
...
}, {
"id" : -1,
"name" : "meta",
"dataTypeName" : "meta_data",
"fieldName" : ":meta",
"position" : 0,
"renderTypeName" : "meta_data",
"format" : { },
"flags" : [ "hidden" ]
}, {
"id" : 45985351,
"name" : "DBN",
"dataTypeName" : "text",
"fieldName" : "dbn",
"position" : 1,
"renderTypeName" : "text",
"tableColumnId" : 8032537,
"cachedContents" : {
"non_null" : "10075",
...
id值为45985351的字典是第一个不将meta_data作为dataTypeName键的值的字典。相反,其值为文本。该字典描述了您最终数据集的第一列信息。该列的元素以文本格式(也就是字符串)呈现。该第一个字典的name键具有DBN值,表示DBN是您的第一列的名称。在此字典之后的所有字典(在columns键中)都不会将meta_data作为他们的dataTypeName键的值,表明所有这些字典都代表DataFrame的下一列的信息。
现在你已经对于在哪里找到有关表格列的信息有了基本的了解,接下来你将会获取这些列的名称。
找到列名
在这个部分,你将把API端点中所有列的名称添加到一个名为”columns”的列表中。将以下代码行添加到Jupyter Notebook的新单元格中,然后运行该单元格。
- response=requests.get(‘https://data.cityofnewyork.us/api/views/ihfw-zy9j/rows.json’).json()
- columns=[]
- for i in response[‘meta’][‘view’][‘columns’]:
- if(i[‘dataTypeName’]==‘meta_data’):
- continue
- else:
- columns.append(i[‘name’])
与您在第2步中的方法3类似,您将通过请求模块读取此URL中的数据,并将其赋值给一个名为response的变量。
在第三行,你定义了列列表。在第五行,你定义了视图键值中的列键,而这个视图键是在元键的值中定义的。
for循环遍历与columns键相关联的列表中的每个字典,并检查这些字典的dataTypeName键的值。如果当前字典的dataTypeName键的值等于meta_data,那么你跳过下一个字典。如果不是,则将name键的值附加到columns列表中。
接下来,检查列列表的内容。在新的单元格中运行以下代码行:
columns
列名将被打印到屏幕上。
[‘DBN’, ‘Name’, ‘schoolyear’, ‘fl_percent’, ‘frl_percent’, ‘total_enrollment’, ‘prek’, ‘k’, ‘grade1’, ‘grade2’, ‘grade3’, ‘grade4’, ‘grade5’, ‘grade6’, ‘grade7’, ‘grade8’, ‘grade9’, ‘grade10’, ‘grade11’, ‘grade12’, ‘ell_num’, ‘ell_percent’, ‘sped_num’, ‘sped_percent’, ‘ctt_num’, ‘selfcontained_num’, ‘asian_num’, ‘asian_per’, ‘black_num’, ‘black_per’, ‘hispanic_num’, ‘hispanic_per’, ‘white_num’, ‘white_per’, ‘male_num’, ‘male_per’, ‘female_num’, ‘female_per’]
输出结果确认,任何数据类型名称非元数据的列都已添加到列列表中。
在这一部分中,您获得了无序API端点中列名的列表,您需要用它们来创建最终的DataFrame。接下来,您将提取属于列列表中定义的列的数据。
定义一个包含n个子列表的列表
在这一部分,你将确定API中的列数,并为该数量定义一个相应的子列表。
最终,您在第二步中使用方法2,从一个字典中获取pandas DataFrame。字典的键表示列名,而这些键的值是代表各列元素的列表。在确定API中的列数后,您将定义一个包含相同列数的子列表的列表,每个子列表包含相应列的数据。
一旦使用这个操作定义了一个字典,你就可以通过循环遍历列列表和子列表的方式动态地向字典中添加内容。例如,索引为0的子列表将作为列名列表中索引为0的列名的元素列表。同样地,索引为1的子列表将作为列名列表中索引为1的列名的元素列表。
在之前的步骤中,你获得了一个列名的列表。现在,你将通过在新单元格中运行此行来检查这个列表的长度。
len(columns)
len (columns) 返回 columns 列表中的项目数量。
API中显示的列数会打印到屏幕上。
38
既然你有38列,你将定义一个包含38个子列表的列表,其中每个子列表都将包含对应列的数据。
在新的单元格中运行以下代码,创建一个包含38个子列表的列表(命名为d)。
d = [[] for x in range(len(columns))]
d
创建列表时,请首先命名它(在这种情况下,命名为d)。数据集中的每个数组([])将创建一个新的子列表,其中包含刚刚找到的列范围内的数据。
下面的输出显示了38个子列表的清单。
[[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
这些子列表最初为空,因为您尚未向其添加任何元素。
你现在已经在列表d中创建了38个子列表。这38个子列表的内容将代表38列的逐列数据。接下来,你将添加这些内容。
获取列的值
在这一部分中,您将从API端点提取按列排列的数据,并将这些数据追加到您在上一步骤中创建的38个子列表中。为了完成这个任务,您首先需要找到整个API中数据键的位置。数据键的值将代表表格的内容。
当在浏览器中访问API URL时,请键入CTRL+F并搜索数据。大约会有73个出现的地方。找到最后一个出现的地方,在那里你会找到以下内容:
...
"data" : [ [ "row-h6zp~rx75.iwiv", "00000000-0000-0000-C205-81EF0C7F0969", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20052006", "89.4", null, "281", "15", "36", "40", "33", "38", "52", "29", "38", null, null, null, null, null, null, "36", "12.8", "57", "20.3", "25", "9", "10", "3.6", "74", "26.3", "189", "67.3", "5", "1.8", "158", "56.2", "123", "43.8" ]
, [ "row-bgbf_ntuw.ckdq", "00000000-0000-0000-9AF5-4D58BAF51C20", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20062007", "89.4", null, "243", "15", "29", "39", "38", "34", "42", "46", null, null, null, null, null, null, null, "38", "15.6", "55", "22.6", "19", "15", "18", "7.4", "68", "28", "153", "63", "4", "1.6", "140", "57.6", "103", "42.4" ]
, [ "row-mspc-8wz5_uxb8", "00000000-0000-0000-9E11-73B99A1B02D9", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20072008", "89.4", null, "261", "18", "43", "39", "36", "38", "47", "40", null, null, null, null, null, null, null, "52", "19.9", "60", "23", "20", "14", "16", "6.1", "77", "29.5", "157", "60.2", "7", "2.7", "143", "54.8", "118", "45.2" ]
, [ "row-p6za~9ikt~ums7", "00000000-0000-0000-D2CD-5904BA5DC16E", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20082009", "89.4", null, "252", "17", "37", "44", "32", "34", "39", "49", null, null, null, null, null, null, null, "48", "19", "62", "24.6", "21", "17", "16", "6.3", "75", "29.8", "149", "59.1", "7", "2.8", "149", "59.1", "103", "40.9" ]
, [ "row-55ss_ktcg~w7ru", "00000000-0000-0000-9425-35118FA9200F", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20092010", " ", "96.5", "208", "16", "40", "28", "32", "30", "24", "38", null, null, null, null, null, null, null, "40", "19.2", "46", "22.1", "14", "14", "16", "7.7", "67", "32.2", "118", "56.7", "6", "2.9", "124", "59.6", "84", "40.4" ]
, [ "row-ugmn-sxmy_fyiu", "00000000-0000-0000-2070-ABC0343F1148", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20102011", " ", "96.5", "203", "13", "37", "35", "33", "30", "30", "25", null, null, null, null, null, null, null, "30", "14.8", "46", "22.7", "21", "9", "13", "6.4", "75", "36.9", "110", "54.2", "4", "2", "113", "55.7", "90", "44.3" ]
, [ "row-a4rf-xqy7~fvkh", "00000000-0000-0000-AF7F-41FA5EB8E91E", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE ", "20112012", null, "89.4", "189", "13", "31", "35", "28", "25", "28", "29", " ", " ", " ", " ", " ", " ", " ", "20", "10.6", "40", "21.2", "23", "7", "12", "6.3", "63", "33.3", "109", "57.7", "4", "2.1", "97", "51.3", "92", "48.7" ]
...
在分配给数据键的列表中,每个子列表表示您潜在DataFrame的一行的值。要确定实际行数据开始的位置,您需要将列数与数据部分的单个子列表中元素的数量进行比较。
在新的单元格中运行以下行代码来确认列数。
len(columns)
以下输出将打印列数。
38
接下来,检查数据中第一个子列表的长度,即第一行的值的数量。
len(response['data'][0])
你可以使用ID号为0来指定数据中的第一个子列表。
以下输出将打印出data中第一个子列表中的元素数量。
46
你可能认为由于数据是按行排列的,第一个子列表(46个元素)的数量应该等于列数(38)。然而,每个子列表的长度都是46,比38多8个。回想一下,在列键中,你找到了8个字典的dataTypeName键的值为meta_data。因此,你在这些子列表开头看到的额外的8个值表示元数据值,而不代表你的表格内容。
由于列表中的索引从0开始,你现在知道在范围0至7(包括)的子列表的值不代表DataFrame的内容,而是表示元数据值。因此,逐行数据的内容从索引8开始。
你将使用循环从索引46-38(8)开始的数据子列表中添加元素,直到索引46。为了测试这个功能,你将仅从第一行中提取数据(而不是从所有子列表中提取所有数据)。在一个新的单元格中运行以下代码行:
count=0
for i in response['data'][0][len(response['data'][0])-len(columns):len(response['data'][0])]: #from index 8 to 45
d[count].append(i)
count=count+1
d
在这种情况下,变量count帮助将第一行的列数据追加到d的第n个子列表上。
然后代码遍历数据中的第一个子列表,从索引8开始获取元素,直到索引45结束(结果为38个元素)。
response[‘data’][0]的长度表示数据键中第一个子列表的长度,而len(columns)是列表columns的长度,表示最后DataFrame的列数。
这是前几次迭代中发生的情况概述。计数最初为0。当您进入for循环时,i是response[‘data’][0][8],即第一个子列表中第一个非元数据值。然后,您将此i值附加到d[0]上;方括号中的0是当前计数值。因此,您将此值附加到列表d中的第一个子列表中。
计数增加1。 当i等于response[‘data’][0][9](第一个子列表中第二个非元数据值)时,将i的值添加到d[1]中(因为在前一次迭代中,计数增加了1,所以0现在是1)。 结果,该值被追加到列表d的第二个子列表中。 这个过程一直持续到达索引45。上述代码的最后一行将显示更新后的d子列表。
输出显示第一行数据。
[[’01M015′], [‘P.S. 015 ROBERTO CLEMENTE’], [‘20052006’], [‘89.4’], [None], [‘281′], [’15’], [’36’], [’40’], [’33’], [’38’], [’52’], [’29’], [’38’], [None], [None], [None], [None], [None], [None], [’36’], [‘12.8′], [’57’], [‘20.3′], [’25’], [‘9′], [’10’], [‘3.6′], [’74’], [‘26.3’], [‘189’], [‘67.3’], [‘5’], [‘1.8’], [‘158’], [‘56.2’], [‘123’], [‘43.8’]]
为了检查第一行是否包含正确的数据,将其与作为API URL中data key的值的列表中的第一个子列表进行比较。
...
"data" : [ [ "row-h6zp~rx75.iwiv", "00000000-0000-0000-C205-81EF0C7F0969", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20052006", "89.4", null, "281", "15", "36", "40", "33", "38", "52", "29", "38", null, null, null, null, null, null, "36", "12.8", "57", "20.3", "25", "9", "10", "3.6", "74", "26.3", "189", "67.3", "5", "1.8", "158", "56.2", "123", "43.8" ]
...
您可能需要向右滑动上方的框以查看完整的数据片段。
前几个值(从索引0到7)代表与元数据列相关的信息。第一个子列表(从索引8到45)中的高亮值是第一行的值,你已成功提取出来。
要为所有行执行相同的任务,您需要更新先前的代码。首先,在新的单元格中运行以下命令重新定义包含38个子列表的d列表:
d = [[] for x in range(len(columns))]
由于子列表d当前包含所有第一行的元素,您正在重新定义d列表。现在,您的目标是对所有剩余行(包括第一行)执行相同的操作。
既然你已经创建了一个包含38个子列表的新列表,请在一个新的单元格中运行以下代码行,它会对潜在的数据框的所有行进行数据提取。
- for i in response[‘data’]:
- count=0
- for j in range(len(response[‘data’][0])–len(columns),len(response[‘data’][0])):
- d[count].append(i[j])
- count+=1
在第一行中,对于response[‘data’]中的每一个子列表,i都被逐一赋值。
这里有几个迭代的概述。你从i开始,将列表中的第一个子列表分配给data键。计数为0。
第3行允许您在子列表的索引8到46之间遍历元素。在Python中,for循环的上限是排他性的,所以您将获得从索引8到45的元素。
“d[count].append(i[j])”的意思是d[0].append(i[8]),意思是将第一个子列表(当前被赋值给i)的索引为8的元素添加到列表d的第一个子列表中。count增加1。您继续在内部循环中。现在j为9。
第4行现在是d[1].append(i[9]),意味着在第一个子列表(仍然根据外循环分配给i)中的索引为9的元素被添加到列表d的第二个子列表中。计数被递增。内循环会继续直到将一行的所有值都添加到列表d的所有子列表中为止。
在内循环结束时,将第一行的最后一个值追加到列表d的操作将如下所示:d[37].append(i[45])。在i的第一次迭代结束时,列表d的状态与先前的输出相同(所有子列表中仅有一个元素)。
当你跳出内循环时,你会进入外循环。此时,i将被赋值为数据键中被分配的列表内的下一个子列表(第二个子列表)。计数值现在重新设为0。
当您在第二次对i进行迭代时,当您跳出内部循环时,d的所有子列表将会有两个值。每个子列表的第二个值代表着属于第二行的数据。这种情况会适用于数据键所分配的所有子列表。
为了让您了解d的子列表中所获取数据的样式,您将检索d的前三个子列表中的前五个元素。在生成的数据框中,这些值将是前三列的前五个元素。
从d的索引0开始,先回顾子列表的前五个元素。
d[0][0:5]
输出将会显示以下内容:
[’01M015′, ’01M015′, ’01M015′, ’01M015′, ’01M015′]
当您在浏览器中查看API的数据部分时,您可以通过与输出进行比较来验证其准确性。
...
"data" : [ [ "row-h6zp~rx75.iwiv", "00000000-0000-0000-C205-81EF0C7F0969", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20052006", "89.4", null, "281", "15", "36", "40", "33", "38", "52", "29", "38", null, null, null, null, null, null, "36", "12.8", "57", "20.3", "25", "9", "10", "3.6", "74", "26.3", "189", "67.3", "5", "1.8", "158", "56.2", "123", "43.8" ]
, [ "row-bgbf_ntuw.ckdq", "00000000-0000-0000-9AF5-4D58BAF51C20", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20062007", "89.4", null, "243", "15", "29", "39", "38", "34", "42", "46", null, null, null, null, null, null, null, "38", "15.6", "55", "22.6", "19", "15", "18", "7.4", "68", "28", "153", "63", "4", "1.6", "140", "57.6", "103", "42.4" ]
, [ "row-mspc-8wz5_uxb8", "00000000-0000-0000-9E11-73B99A1B02D9", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20072008", "89.4", null, "261", "18", "43", "39", "36", "38", "47", "40", null, null, null, null, null, null, null, "52", "19.9", "60", "23", "20", "14", "16", "6.1", "77", "29.5", "157", "60.2", "7", "2.7", "143", "54.8", "118", "45.2" ]
, [ "row-p6za~9ikt~ums7", "00000000-0000-0000-D2CD-5904BA5DC16E", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20082009", "89.4", null, "252", "17", "37", "44", "32", "34", "39", "49", null, null, null, null, null, null, null, "48", "19", "62", "24.6", "21", "17", "16", "6.3", "75", "29.8", "149", "59.1", "7", "2.8", "149", "59.1", "103", "40.9" ]
, [ "row-55ss_ktcg~w7ru", "00000000-0000-0000-9425-35118FA9200F", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20092010", " ", "96.5", "208", "16", "40", "28", "32", "30", "24", "38", null, null, null, null, null, null, null, "40", "19.2", "46", "22.1", "14", "14", "16", "7.7", "67", "32.2", "118", "56.7", "6", "2.9", "124", "59.6", "84", "40.4" ]
,
...
你可能需要向上滚动到上面的框中的右侧才能查看完整的数据片段。
您获取的数值与突出显示的数值相匹配,这些数值位于每个子列表的索引8处。这些子列表中索引范围0-7(包括)的元素表示元数据值,而不是潜在的DataFrame数值。最终,这些数值将代表索引0的列的前五个元素。
接下来,你可以回顾一下d中索引为1的子列表的前五个元素。
d[1][0:5]
第二个子列表的前五个元素会打印在屏幕上。
[‘P.S. 015 ROBERTO CLEMENTE’, ‘P.S. 015 ROBERTO CLEMENTE’, ‘P.S. 015 ROBERTO CLEMENTE’, ‘P.S. 015 ROBERTO CLEMENTE’, ‘P.S. 015 ROBERTO CLEMENTE’]
这些值表示列索引1的列列表中的前五个元素:名称。使用API URL的数据部分验证输出。
...
"data" : [ [ "row-h6zp~rx75.iwiv", "00000000-0000-0000-C205-81EF0C7F0969", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20052006", "89.4", null, "281", "15", "36", "40", "33", "38", "52", "29", "38", null, null, null, null, null, null, "36", "12.8", "57", "20.3", "25", "9", "10", "3.6", "74", "26.3", "189", "67.3", "5", "1.8", "158", "56.2", "123", "43.8" ]
, [ "row-bgbf_ntuw.ckdq", "00000000-0000-0000-9AF5-4D58BAF51C20", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20062007", "89.4", null, "243", "15", "29", "39", "38", "34", "42", "46", null, null, null, null, null, null, null, "38", "15.6", "55", "22.6", "19", "15", "18", "7.4", "68", "28", "153", "63", "4", "1.6", "140", "57.6", "103", "42.4" ]
, [ "row-mspc-8wz5_uxb8", "00000000-0000-0000-9E11-73B99A1B02D9", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20072008", "89.4", null, "261", "18", "43", "39", "36", "38", "47", "40", null, null, null, null, null, null, null, "52", "19.9", "60", "23", "20", "14", "16", "6.1", "77", "29.5", "157", "60.2", "7", "2.7", "143", "54.8", "118", "45.2" ]
, [ "row-p6za~9ikt~ums7", "00000000-0000-0000-D2CD-5904BA5DC16E", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20082009", "89.4", null, "252", "17", "37", "44", "32", "34", "39", "49", null, null, null, null, null, null, null, "48", "19", "62", "24.6", "21", "17", "16", "6.3", "75", "29.8", "149", "59.1", "7", "2.8", "149", "59.1", "103", "40.9" ]
, [ "row-55ss_ktcg~w7ru", "00000000-0000-0000-9425-35118FA9200F", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20092010", " ", "96.5", "208", "16", "40", "28", "32", "30", "24", "38", null, null, null, null, null, null, null, "40", "19.2", "46", "22.1", "14", "14", "16", "7.7", "67", "32.2", "118", "56.7", "6", "2.9", "124", "59.6", "84", "40.4" ]
,
...
您可能需要向右滚动框上方才能查看完整的数据片段。您所获取的数值与突出显示的数值相符。
最后,回顾一下d中索引2处子列表的前五个元素。
d[2][0:5]
以下输出将在屏幕上打印:
[‘20052006’, ‘20062007’, ‘20072008’, ‘20082009’, ‘20092010’]
通过API URL的数据部分验证输出。
...
"data" : [ [ "row-h6zp~rx75.iwiv", "00000000-0000-0000-C205-81EF0C7F0969", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20052006", "89.4", null, "281", "15", "36", "40", "33", "38", "52", "29", "38", null, null, null, null, null, null, "36", "12.8", "57", "20.3", "25", "9", "10", "3.6", "74", "26.3", "189", "67.3", "5", "1.8", "158", "56.2", "123", "43.8" ]
, [ "row-bgbf_ntuw.ckdq", "00000000-0000-0000-9AF5-4D58BAF51C20", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20062007", "89.4", null, "243", "15", "29", "39", "38", "34", "42", "46", null, null, null, null, null, null, null, "38", "15.6", "55", "22.6", "19", "15", "18", "7.4", "68", "28", "153", "63", "4", "1.6", "140", "57.6", "103", "42.4" ]
, [ "row-mspc-8wz5_uxb8", "00000000-0000-0000-9E11-73B99A1B02D9", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20072008", "89.4", null, "261", "18", "43", "39", "36", "38", "47", "40", null, null, null, null, null, null, null, "52", "19.9", "60", "23", "20", "14", "16", "6.1", "77", "29.5", "157", "60.2", "7", "2.7", "143", "54.8", "118", "45.2" ]
, [ "row-p6za~9ikt~ums7", "00000000-0000-0000-D2CD-5904BA5DC16E", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20082009", "89.4", null, "252", "17", "37", "44", "32", "34", "39", "49", null, null, null, null, null, null, null, "48", "19", "62", "24.6", "21", "17", "16", "6.3", "75", "29.8", "149", "59.1", "7", "2.8", "149", "59.1", "103", "40.9" ]
, [ "row-55ss_ktcg~w7ru", "00000000-0000-0000-9425-35118FA9200F", 0, 1425758466, null, 1425758466, null, "{ }", "01M015", "P.S. 015 ROBERTO CLEMENTE", "20092010", " ", "96.5", "208", "16", "40", "28", "32", "30", "24", "38", null, null, null, null, null, null, null, "40", "19.2", "46", "22.1", "14", "14", "16", "7.7", "67", "32.2", "118", "56.7", "6", "2.9", "124", "59.6", "84", "40.4" ]
,
...
您可能需要向右滚动上方的框才能查看完整的数据片段。您获得的值与高亮显示的值匹配。这些值最终表示列索引2处列(schoolyear)的前五个元素。
当你完成这一步骤时,你已成功提取了所有38列中的所有行的数据,并将它们添加到列表d的38个子列表中。现在,你已经准备好进行最后一步,从迄今为止获得的数据创建一个pandas DataFrame。
为数据帧创建一个字典。
目前为止,您已经提取了38列中每一列的名称和值,并将其以字典格式传递给pandas DataFrame。在本节中,您将创建一个字典,键是从列列表中获取的列名,其值将是从d中获取的38个子列表。
在新的单元格中运行下面的代码。
json_dict={}
for i in range(0,len(columns)):
json_dict.update({columns[i]:d[i]})
你循环遍历从0到(但不包括)38的范围,并将键值对追加到最终的字典json_dict中,然后将其传递给pandas DataFrame用于转换。所以,例如,当i为0时,你将d中索引为0的子列表分配为键DBN的值,该键由columns[0]表示,并对于其余的索引也适用。
为了了解字典中获得的键的样子,请查看json_dict中存在的所有键。
json_dict.keys()
最后字典的键列表将会显示在屏幕上。
dict_keys([‘DBN’, ‘Name’, ‘schoolyear’, ‘fl_percent’, ‘frl_percent’, ‘total_enrollment’, ‘prek’, ‘k’, ‘grade1’, ‘grade2’, ‘grade3’, ‘grade4’, ‘grade5’, ‘grade6’, ‘grade7’, ‘grade8’, ‘grade9’, ‘grade10’, ‘grade11’, ‘grade12’, ‘ell_num’, ‘ell_percent’, ‘sped_num’, ‘sped_percent’, ‘ctt_num’, ‘selfcontained_num’, ‘asian_num’, ‘asian_per’, ‘black_num’, ‘black_per’, ‘hispanic_num’, ‘hispanic_per’, ‘white_num’, ‘white_per’, ‘male_num’, ‘male_per’, ‘female_num’, ‘female_per’])
38列子列表d已经被分配给上述的38个键在json_dict中。因此,您得到了一个字典,其中键是列名,值是包含每个列元素的列表。
现在将这个字典传递给pandas来生成一个DataFrame。这个DataFrame可以使用您选择的变量名进行调用(本教程使用的是data)。
data=pd.DataFrame(json_dict)
接下来,回顾一部分结果数据框。
data.head()
DataFrame的前五行将会打印到屏幕上。
DBN Name schoolyear fl_percent frl_percent total_enrollment prek k grade1 grade2 … black_num black_per hispanic_num hispanic_per white_num white_per male_num male_per female_num female_per 0 01M015 P.S. 015 ROBERTO CLEMENTE 20052006 89.4 None 281 15 36 40 33 … 74 26.3 189 67.3 5 1.8 158 56.2 123 43.8 1 01M015 P.S. 015 ROBERTO CLEMENTE 20062007 89.4 None 243 15 29 39 38 … 68 28 153 63 4 1.6 140 57.6 103 42.4 2 01M015 P.S. 015 ROBERTO CLEMENTE 20072008 89.4 None 261 18 43 39 36 … 77 29.5 157 60.2 7 2.7 143 54.8 118 45.2 3 01M015 P.S. 015 ROBERTO CLEMENTE 20082009 89.4 None 252 17 37 44 32 … 75 29.8 149 59.1 7 2.8 149 59.1 103 40.9 4 01M015 P.S. 015 ROBERTO CLEMENTE 20092010 96.5 208 16 40 28 32 … 67 32.2 118 56.7 6 2.9 124 59.6 84 40.4
你可能需要向右滚动以查看上方框中完整的数据框头部。通过提取的数据创建数据框已经成功完成。
总结
在本教程中,您使用了一些常见的方法,从不同格式的字典和URL 创建了一个 pandas DataFrame。您还创建了一个来自 API 端点的 DataFrame,其中列和它们对应的值不是以直观的方式显示。
这个教程应该帮助你理解如何使用独特的方法从非常规的API端点或字典中提取数据来创建数据框。如果想了解更多关于pandas数据框的内容,请查看我们的pandas教程。