熊猫将嵌套值与其他列一起切片
Posted
技术标签:
【中文标题】熊猫将嵌套值与其他列一起切片【英文标题】:pandas slicing nested values along with other columns 【发布时间】:2016-06-02 01:12:18 【问题描述】:我正在尝试从下面的 json 数据中获取嵌套值。
"region_id": 60763,
"phone": "",
"address":
"region": "NY",
"street-address": "147 West 43rd Street",
"postal-code": "10036",
"locality": "New York City"
,
"id": 113317,
"name": "Casablanca Hotel Times Square"
"region_id": 32655,
"phone": "",
"address":
"region": "CA",
"street-address": "300 S Doheny Dr",
"postal-code": "90048",
"locality": "Los Angeles"
,
"id": 76049,
"name": "Four Seasons Hotel Los Angeles at Beverly Hills"
我刚刚将上述数据加载到我的熊猫数据框中,使用:
with open("file path") as f:
df = pd.DataFrame(json.loads(line) for line in f)
现在我的数据框如下所示:
address Phone
0 u'region': u'NY', u'street-address': u'147 We...
1 u'region': u'CA', u'street-address': u'300 S ...
id name region_id
0 113317 Casablanca Hotel Times Square 60763
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills 32655
我可以使用这个来获取列子集 - data = df[['id', 'name']]
但不知道如何获取region
和street-address
以及id
和name
的值。我的输出数据框应该有id, name, region, street-address
。
注意:我试图弹出并将这个嵌套列address
与我的数据框连接起来。但是由于我的数据很大 - 348MB,当我尝试按列 - (轴 - 1)时,连接会占用大量内存。
另外,我正在寻找一种有效的方法来处理这个问题,我是否应该使用将直接使用 C 扩展的 Numpy。或者写入一些数据库,比如 MongoDB。我正在考虑这个,因为在对这些数据进行子集化后,我需要根据 id 列加入这个其他数据集以获取其他几个字段。
【问题讨论】:
【参考方案1】:一个小的辅助函数就可以解决问题:
def get_entries(line):
data = json.loads(line)
res = k: data[k] for k in ['id', 'name']
res.update(k: data['address'][k] for k in ['region', 'street-address'])
return res
with open("file path") as f:
df = pd.DataFrame(get_entries(line) for line in f)
输出:
id name region \
0 113317 Casablanca Hotel Times Square NY
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills CA
street-address
0 147 West 43rd Street
1 300 S Doheny Dr
或者,更好看一点:
【讨论】:
你有同样的想法,但你更快。我对你的回答投了赞成票。【参考方案2】:以下方法可行(不过,我在下面添加了一个更有效的解决方案;只需向下滚动到 EDIT):
import pandas as pd
# read the updated json file
df = pd.read_json('data.json')
# convert column with the nested json structure
tempdf = pd.concat([pd.DataFrame.from_dict(item, orient='index').T for item in df.address])
# get rid of the converted column
df.drop('address', 1, inplace=True)
# prepare concat
tempdf.index = df.index
# merge the two dataframes back together
df = pd.concat([df, tempdf], axis=1)
输出:
id name phone region_id \
0 113317 Casablanca Hotel Times Square 60763
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills 32655
region street-address postal-code locality
0 NY 147 West 43rd Street 10036 New York City
1 CA 300 S Doheny Dr 90048 Los Angeles
现在您可以使用drop
命令删除不需要的列。
我修改了实际上无效的 json 文件;你可以检查一下,例如在JSONLint:
[
"region_id": 60763,
"phone": "",
"address":
"region": "NY",
"street-address": "147 West 43rd Street",
"postal-code": "10036",
"locality": "New York City"
,
"id": 113317,
"name": "Casablanca Hotel Times Square"
,
"region_id": 32655,
"phone": "",
"address":
"region": "CA",
"street-address": "300 S Doheny Dr",
"postal-code": "90048",
"locality": "Los Angeles"
,
"id": 76049,
"name": "Four Seasons Hotel Los Angeles at Beverly Hills"
]
编辑
在@MaxU 的回答(对我不起作用)的基础上,您还可以执行以下操作:
import pandas as pd
import ujson
from pandas.io.json import json_normalize
# this is the json file from above
with open('data.json') as f:
data = ujson.load(f)
现在,按照@MaxU 的建议,您可以使用json_normalize 摆脱嵌套结构:
df3 = json_normalize(data)
这给了你:
address.locality address.postal-code address.region address.street-address id name phone region_id
0 New York City 10036 NY 147 West 43rd Street 113317 Casablanca Hotel Times Square 60763
1 Los Angeles 90048 CA 300 S Doheny Dr 76049 Four Seasons Hotel Los Angeles at Beverly Hills 32655
您可以像这样重命名要保留的列:
df3.rename(columns='address.region': 'region', 'address.street-address': 'street-address', inplace=True)
然后选择您要保留的列:
df3 = df3[['id', 'name', 'region', 'street-address']]
给你想要的输出:
id name region street-address
0 113317 Casablanca Hotel Times Square NY 147 West 43rd Street
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills CA 300 S Doheny Dr
【讨论】:
【参考方案3】:原生 Pandas 解决方案 - pandas.io.json.json_normalize():
更正和工作版本:
import ujson
import pandas as pd
from pandas.io.json import json_normalize
pd.set_option('display.expand_frame_repr', False)
with open('aaa') as f:
data = ujson.load(f)
df = json_normalize(data)[['id', 'name', 'address.region', 'address.street-address']].rename(columns='address.region': 'region', 'address.street-address': 'street-address')
print(df)
输出:
id name region street-address
0 113317 Casablanca Hotel Times Square NY 147 West 43rd Street
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills CA 300 S Doheny Dr
NOT WORKING 版本(正如 Cleb 指出的那样):
import ujson
from pandas.io.json import json_normalize
with open('data.json') as f:
data = ujson.load(f)
df = json_normalize(data, 'address', ['region', 'street-address'])
pd.set_option('display.expand_frame_repr', False)
print(df)
或者,您可以使用ujson
(超快速 JSON)来生成字典列表,然后从中生成一个 DataFrame:
import ujson
import pandas as pd
data_list = []
with open('data.json') as f:
for line in f:
d = ujson.loads(line)
data_list.append(
"id":d["id"],
"name":d["name"],
"region":d["address"]["region"],
"street-address":d["address"]["street-address"]
)
df = pd.DataFrame(data_list)
pd.set_option('display.expand_frame_repr', False)
print(df)
我不知道哪种解决方案会更高效/更快 - 请根据您的真实数据 (348MiB) 尝试一下,并给我们一个简短的反馈。
PS 如果可能的话,只调用一次 pd.DataFrame/pd.read_json ,否则会慢很多。
输出:
id name region street-address
0 113317 Casablanca Hotel Times Square NY 147 West 43rd Street
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills CA 300 S Doheny Dr
【讨论】:
您的第一个方法看起来很棒!但是,我得到KeyError: 'region'
。可能是什么原因?
我使用您的方法更新了我的答案,但由于您的脚本无法在我的计算机上运行,因此稍作修改。我赞成您的回答,因为我真的很喜欢这种方法并且之前没有听说过json_normalize
。
@Cleb,你是对的。感谢您指出这一点!我测试了另一个(旧)脚本,而不是这个。我会修复它并更新我的答案。
@Cleb 谢谢你们俩。我尝试了这两种选择。不知道为什么,但是当我在代码运行 125 秒左右后尝试正常化时,我不断收到关键错误。但是,ujson 很棒。它只需 10 秒即可运行整个文件,并且可以正常工作。再次感谢。
@Jeeva:很高兴它成功了。但是,您仍然收到密钥错误,这很奇怪;它既不应该出现在 MaxU 的更正版本中,也不应该出现在我下面的版本中(你试过那个)?【参考方案4】:
首先,使用df
的id
和name
列创建一个新数据框。然后遍历每个目标字段(都位于address
)并应用lambda
函数从字典中获取项目。
df2 = df[['id', 'name']]
for col in ['region', 'street-address']:
df2[col] = df.address.apply(lambda j: j.get(col))
>>> df2
id name region street-address
0 113317 Casablanca Hotel Times Square NY 147 West 43rd Street
1 76049 Four Seasons Hotel Los Angeles at Beverly Hills CA 300 S Doheny Dr
【讨论】:
以上是关于熊猫将嵌套值与其他列一起切片的主要内容,如果未能解决你的问题,请参考以下文章