老瓜

老瓜 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

老瓜 赞了文章 · 2020-11-11

骚操作!嵌套 JSON 秒变 Dataframe!

image

首发于公众号:Python数据科学

作者:东哥起飞

调用API和文档数据库会返回嵌套的JSON对象,当我们使用Python尝试将嵌套结构中的键转换为列时,数据加载到pandas中往往会得到如下结果:

df = pd.DataFrame.from_records(results [“ issues”],columns = [“ key”,“ fields”])
说明:这里results是一个大的字典,issues是results其中的一个键,issues的值为一个嵌套JSON对象字典的列表,后面会看到JSON嵌套结构。

问题在于API返回了嵌套的JSON结构,而我们关心的键在对象中确处于不同级别。

嵌套的JSON结构张成这样的。

而我们想要的是下面这样的。

下面以一个API返回的数据为例,API通常包含有关字段的元数据。假设下面这些是我们想要的字段。

  • key:JSON密钥,在第一级的位置。
  • summary:第二级的“字段”对象。
  • status name:第三级位置。
  • statusCategory name:位于第4个嵌套级别。

如上,我们选择要提取的字段在issues列表内的JSON结构中分别处于4个不同的嵌套级别,一环扣一环。

{
  "expand": "schema,names",
  "issues": [
    {
      "fields": {
        "issuetype": {
          "avatarId": 10300,
          "description": "",
          "id": "10005",
          "name": "New Feature",
          "subtask": False
        },
        "status": {
          "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
          "id": "5",
          "name": "Resolved",
          "statusCategory": {
            "colorName": "green",
            "id": 3,
            "key": "done",
            "name": "Done",
          }
        },
        "summary": "Recovered data collection Defraglar $MFT problem"
      },
      "id": "11861",
      "key": "CAE-160",
    },
    {
      "fields": { 
... more issues],
  "maxResults": 5,
  "startAt": 0,
  "total": 160
}

一个不太好的解决方案

一种选择是直接撸码,写一个查找特定字段的函数,但问题是必须对每个嵌套字段调用此函数,然后再调用.applyDataFrame中的新列。

为获取我们想要的几个字段,首先我们提取fields键内的对象至列:

df = (
    df["fields"]
    .apply(pd.Series)
    .merge(df, left_index=True, right_index = True)
)

从上表看出,只有summary是可用的,issuetype、status等仍然埋在嵌套对象中。

下面是提取issuetype中的name的一种方法。

# 提取issue type的name到一个新列叫"issue_type"
df_issue_type = (
    df["issuetype"]
    .apply(pd.Series)
    .rename(columns={"name": "issue_type_name"})["issue_type_name"]
)
df = df.assign(issue_type_name = df_issue_type)

像上面这样,如果嵌套层级特别多,就需要自己手撸一个递归来实现了,因为每层嵌套都需要调用一个像上面解析并添加到新列的方法。

对于编程基础薄弱的朋友,手撸一个其实还挺麻烦的,尤其是对于数据分析师,着急想用数据的时候,希望可以快速拿到结构化的数据进行分析。

下面东哥分享一个pandas的内置解决方案。

内置的解决方案

pandas中有一个牛逼的内置功能叫 .json_normalize

pandas的文档中提到:将半结构化JSON数据规范化为平面表。

前面方案的所有代码,用这个内置功能仅需要3行就可搞定。步骤很简单,懂了下面几个用法即可。

确定我们要想的字段,使用 . 符号连接嵌套对象。

将想要处理的嵌套列表(这里是results["issues"])作为参数放进 .json_normalize 中。

过滤我们定义的FIELDS列表。

FIELDS = ["key", "fields.summary", "fields.issuetype.name", "fields.status.name", "fields.status.statusCategory.name"]
df = pd.json_normalize(results["issues"])
df[FIELDS]

没错,就这么简单。

其它操作

记录路径

除了像上面那样传递results["issues"]列表之外,我们还使用record_path参数在JSON对象中指定列表的路径。

# 使用路径而不是直接用results["issues"]
pd.json_normalize(results, record_path="issues")[FIELDS]

自定义分隔符

还可以使用sep参数自定义嵌套结构连接的分隔符,比如下面将默认的“.”替换“-”。

### 用 "-" 替换默认的 "."
FIELDS = ["key", "fields-summary", "fields-issuetype-name", "fields-status-name", "fields-status-statusCategory-name"]
pd.json_normalize(results["issues"], sep = "-")[FIELDS]

控制递归

如果不想递归到每个子对象,可以使用max_level参数控制深度。在这种情况下,由于statusCategory.name字段位于JSON对象的第4级,因此不会包含在结果DataFrame中。

# 只深入到嵌套第二级
pd.json_normalize(results, record_path="issues", max_level = 2)

下面是.json_normalizepandas官方文档说明,如有不明白可自行学习,本次东哥就介绍到这里。

pandas官方文档:https://pandas.pydata.org/pan...

原创不易,觉得不错点个赞。

欢迎关注我的个人公众号:Python数据科学

数据科学学习网站:datadeepin

查看原文

赞 2 收藏 1 评论 1

老瓜 回答了问题 · 2020-11-01

解决pandas中,新增一列,如何更好的实现数值型ip到字符型ip的转换

pandas 里有可以接受函数的方法 apply 不需要自己写循环

# 新增列,ip_f
df['ip_f'] = df.ip_int.apply(num2ip)

关注 2 回答 1

老瓜 回答了问题 · 2020-10-27

怎样在dataframe中添加一列,使其值等于所在行的index值的两倍?

你不是已经写出来了吗 - -
df['new']=df.index * 2

关注 2 回答 1

老瓜 回答了问题 · 2020-10-23

解决pandas 字典转换成dataFrame,如何将索引放在第一行

只需要转置一下就可以达到你要的效果

df = pd.DataFrame(res).T
df
    age    name
0    18    zhangsan
1    24    lisi

关注 2 回答 1

老瓜 收藏了文章 · 2020-10-17

再见,可视化!你好,Pandas!

image

来源:Python数据科学
作者:东哥起飞

Python做数据分析离不开pandaspnadas更多的承载着处理和变换数据的角色,pands中也内置了可视化的操作,但效果很糙。

因此,大家在用Python做数据分析时,正常的做法是用先pandas先进行数据处理,然后再用MatplotlibSeabornPlotlyBokeh等对dataframe或者series进行可视化操作。

但是说实话,每个可视化包都有自己独特的方法和函数,经常忘,这是让我一直很头疼的地方。

好消息来了!从最新的pandas版本0.25.3开始,不再需要上面的操作了,数据处理和可视化完全可以用pandas一个就全部搞定。

pandas现在可以使用PlotlyBokeh作为可视化的backend,直接实现交互性操作,无需再单独使用可视化包了。

下面我们一起看看如何使用。

1. 激活backend

importpandas之后,直接使用下面这段代码激活backend,比如下面要激活plotly

pd.options.plotting.backend = 'plotly'

目前,pandas的backend支持以下几个可视化包。

  • Plotly
  • Holoviews
  • Matplotlib
  • Pandas_bokeh
  • Hyplot

2. Plotly backend

Plotly的好处是,它基于Javascript版本的库写出来的,因此生成的Web可视化图表,可以显示为HTML文件或嵌入基于Python的Web应用程序中。

下面看下如何用plotly作为pandas的backend进行可视化。

如果还没安装Plotly,则需要安装它pip intsall plotly。如果是在Jupyterlab中使用Plotly,那还需要执行几个额外的安装步骤来显示可视化效果。

首先,安装IPywidgets

pip install jupyterlab "ipywidgets>=7.5"

然后运行此命令以安装Plotly扩展。

jupyter labextension install jupyterlab-plotly@4.8.1

示例选自openml.org的的数据集,链接如下:

数据链接:https://www.openml.org/d/187

这个数据也是Scikit-learn中的样本数据,所以也可以使用以下代码将其直接导入。

import pandas as pd
import numpy as np

from sklearn.datasets import fetch_openml

pd.options.plotting.backend = 'plotly'

X,y = fetch_openml("wine", version=1, as_frame=True, return_X_y=True)
data = pd.concat([X,y], axis=1)
data.head()

该数据集是葡萄酒相关的,包含葡萄酒类型的许多功能和相应的标签。数据集的前几行如下所示。

image

下面使用Plotly backend探索一下数据集。

绘图方式与正常使用Pandas内置的绘图操作几乎相同,只是现在以丰富的Plotly显示可视化效果。

下面的代码绘制了数据集中两个要素之间的关系。

fig = data[['Alcohol', 'Proline']].plot.scatter(y='Alcohol', x='Proline')
fig.show()

image

如果将鼠标悬停在图表上,可以选择将图表下载为高质量的图像文件。
image

我们可以结合Pandasgroupby函数创建一个条形图,总结各类之间Hue的均值差异。

data[['Hue','class']].groupby(['class']).mean().plot.bar()

image

class添加到我们刚才创建的散点图中。通过Plotly可以轻松地为每个类应用不同的颜色,以便直观地看到分类。

fig = data[['Hue', 'Proline', 'class']].plot.scatter(x='Hue', y='Proline', color='class', title='Proline and Hue by wine class')
fig.show()

image

3. Bokeh backend

Bokeh是另一个Python可视化包,也可提供丰富的交互式可视化效果。Bokeh还具有streaming API,可以为比如金融市场等流数据创建实时可视化。

pandas-Bokeh的GitHub链接如下:

https://github.com/PatrikHlob...

老样子,用pip安装即可,pip install pandas-bokeh

为了在Jupyterlab中显示Bokeh可视化效果,还需要安装两个新的扩展。

jupyter labextension install @jupyter-widgets/jupyterlab-manager
jupyter labextension install @bokeh/jupyter_bokeh

下面我们使用Bokeh backend重新创建刚刚plotly实现的的散点图。

pd.options.plotting.backend = 'pandas_bokeh'

import pandas_bokeh
from bokeh.io import output_notebook
from bokeh.plotting import figure, show

output_notebook()
p1 = data.plot_bokeh.scatter(x='Hue', 
                              y='Proline', 
                              category='class', 
                              title='Proline and Hue by wine class',
                              show_figure=False)
show(p1)

关键语句就一行代码,非常快捷,交互式效果如下。

image

Bokeh还具有plot_grid函数,可以为多个图表创建类似于仪表板的布局,下面在网格布局中创建了四个图表。

output_notebook()

p1 = data.plot_bokeh.scatter(x='Hue', 
                              y='Proline', 
                              category='class', 
                              title='Proline and Hue by wine class',
                              show_figure=False)

p2 = data[['Hue','class']].groupby(['class']).mean().plot.bar(title='Mean Hue per Class')

df_hue = pd.DataFrame({
    'class_1': data[data['class'] == '1']['Hue'],
    'class_2': data[data['class'] == '2']['Hue'],
    'class_3': data[data['class'] == '3']['Hue']},
    columns=['class_1', 'class_2', 'class_3'])

p3 = df_hue.plot_bokeh.hist(title='Distribution per Class: Hue')

df_proline = pd.DataFrame({
    'class_1': data[data['class'] == '1']['Proline'],
    'class_2': data[data['class'] == '2']['Proline'],
    'class_3': data[data['class'] == '3']['Proline']},
    columns=['class_1', 'class_2', 'class_3'])

p4 = df_proline.plot_bokeh.hist(title='Distribution per Class: Proline')

pandas_bokeh.plot_grid([[p1, p2], 
                        [p3, p4]], plot_width=450)

可以看到,可视化的部分都是在pandasdataframe基础上一行代码搞定,最后plot_grid完成布局。
image

4. 总结

在内置的Pandas绘图功能增加多个第三方可视化backend,大大增强了pandas用于数据可视化的功能,今后可能真的不需再去学习众多可视化操作了,使用pandas也可以一击入魂!


原创不易,来波点赞支持。

本篇首发于我的原创公众号:Python数据科学,欢迎关注。
个人网站:http://www.datadeepin.com/

查看原文

老瓜 回答了问题 · 2020-10-09

解决python pandas 分组统计多出一列

需要先去重,再分组统计

user_df.drop_duplicates().groupby(['goods_price'], as_index=False)['user_id'].count()

关注 3 回答 2

老瓜 回答了问题 · 2020-09-26

解决python pandas 这样的数据怎么处理?

df = pd.DataFrame(res)

df[df.pay_type == 2].groupby(['user_id', 'pay_type', 'create_time'], as_index=False)['money'].sum().to_dict('records')

关注 2 回答 1

老瓜 回答了问题 · 2020-07-12

解决请教 pandas的dataframe中,如何取某一列<每个>数据最后2位?

name 和 type 都是字符串取后两位的方法都一样

df['name'] = df['name'].str[-2:]

df['type'] = df['type'].str[-2:]

price 是 float 类型转换成 int 就会丢弃小数点后面的值了

df['price'] = df['price'].astype(int)

价格如果直接丢弃小数点后的值会产生误差可以先四舍五入一下再转换

df['price'] = df['price'].round(0).astype(int)

关注 2 回答 1

老瓜 回答了问题 · 2020-07-11

dataframe放在字典循环里面无效

你的逻辑错了,你遍历的是字典 每循环一次都会使整个 dataframe 都更新一次,所以你得到的结果实际上是字典里最一个被遍历的数据。

我的写法是 先写一个查找 key 的函数

def find_key(x):
    for key, values in study_list_by_ta.items():
        if x in values:
            return key
    return None

再将函数代入

signal_history_cal1["TA"] = signal_history_cal1['Study No.'].apply(find_key)

关注 2 回答 1

老瓜 回答了问题 · 2020-07-06

解决pandas dataframe如何转换成列表,要有完整的表头和index

image.png
如图, df.values 是不包括 columns 和 index 的,所以 df.values.tolist() 自然也不会包括。

想要里面包括 index 可以用 reset_index 重置索引后再转换

df.reset_index().values.tolist()

想要里面包括 columns 稍微麻烦点,先转置再重置索引再转置回来

df.T.reset_index().T.values.tolist()

同时包括 index 和 columns 也是同样的原理

df.reset_index().T.reset_index().T.values.tolist()

关注 2 回答 2

认证与成就

  • 获得 27 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-01-03
个人主页被 644 人浏览