Python数据科学

Python数据科学 查看完整档案

北京编辑  |  填写毕业院校互联网金融  |  数据挖掘工程师 编辑 www.datadeepin.com 编辑
编辑

微信公众号:Python数据科学

知乎专栏:Python数据分析

个人网站:http://www.datadeepin.com

个人动态

Python数据科学 收藏了文章 · 10月16日

再见,可视化!你好,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/

查看原文

Python数据科学 发布了文章 · 10月16日

再见,可视化!你好,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/

查看原文

赞 18 收藏 12 评论 1

Python数据科学 收藏了文章 · 9月29日

安利 5 个拍案叫绝的 Matplotlib 骚操作!

公众号:Python数据科学
作者:东哥起飞

大家都知道,Matplotlib是Python的可视化库,功能很强,可以绘制各种图。一些常规用法前不久分享过Matplotlib官方出品的cheatsheet:Matplotlib官方小抄手册公开,配套可视化代码已打包!

但是!今天我们不走寻常路,专挑几个贼骚的操作分享下.

1. Span Selector

Span SelectorMatplotlib中的鼠标小部件,widgets是用于包含一些交互功能的python对象。Span Selector可以通过鼠标框选,方便地查看选定区域的最大值和最小值。

下面是代码,首先创建一个基本折线图作为例子。然后,我们调用SpanSelector方法并使用它来选择一个区域,然后在该区域中显示最大值和最小值。

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
def onselect(xmin, xmax):
    print(xmin, xmax)
    return xmin, xmax
fig, ax = plt.subplots()
ax.plot([1,2,3,4,5,6,7], [10, 50, 100, 23,15,28,45])
span = SpanSelector(ax, onselect, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red'))       
plt.show()

下面是具体操作。
image

2. Broken Barh

Broken的水平条形图是不连续具有间隙的图,它可用于数据值相差很大的情况下,例如,包含极端温度范围的数据集。在这种情况下,Broken的水平条形图非常合适,因为它们可以同时绘制最大和最小范围。

python模块matplotlib.broken_barh()用于绘制Broken的水平条形图。

import matplotlib.pyplot as plt 
#Defining the x and y ranges 
xranges = [(5,5), (20,5),(20,7)] 
yrange = (2,1) 
#Plotting the broken bar chart 
plt.broken_barh(xranges, yrange, facecolors='green') 
xranges = [(6,2), (17,5),(50,2)] 
yrange = (15,1) 
plt.broken_barh(xranges, yrange, facecolors='orange') 
xranges = [(5,2), (28,5),(40,2)] 
yrange = (30,1) 
plt.broken_barh(xranges, yrange, facecolors='red') 
plt.xlabel('Sales') 
plt.ylabel('Days of the Month') 
plt.show()

image

3. Table Demo

Matplotlib的表格功能也是可以在图中显示表格的。当我们希望以条形图的形式快速查看表格中的值时,这特别方便。表格可以放置在图表的顶部,底部或侧面。

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
x = np.random.rand(5, 8)*.7 
plt.plot(x.mean(axis=0), '-o', label='average per column') 
plt.xticks([]) 
plt.table(cellText=[['%1.2f' % xxx for xxx in xx] for xx in x],cellColours=plt.cm.GnBu(x),loc='bottom') 
plt.show()

image
image

4. Watermark Images

有时候我们觉得可视化的背景太单调了,想增加点趣味性,比如将与数据相关的图像作为水印覆盖到可视化图形上。下面就以NBA的詹皇为例试试水,最后呈现出詹皇的数据,同时背景是詹皇本人。

首先,导入要用的数据集,图片和必要的库pandas

import numpy as np 
import matplotlib.image as image 
import matplotlib.pyplot as plt 
import pandas as pd 
df = pd.read_csv('income.csv') 
im = image.imread('Lebron_James.jpeg') # Image

pandas过滤掉仅由勒布朗组成的数据。

lebron_james = df[df['Name']=='LeBron James']

然后像下面这样操作,使用figimage添加水印就ok了。

fig, ax = plt.subplots() 
ax.grid() 
ax.plot('Year','earnings ($ million)',data=lebron_james) 
ax.set_title("LeBron James earnings in US$(millions)") 
fig.figimage(im, 60, 40,cmap='ocean', alpha=.2) 
plt.show()

image

5. XKCD Plots

下面这个操作更有趣味性(更骚)。

如果你想让Matplotlib图上添加一些扭曲,可以简单地xkcd()pyplot对象上调用方法,如下所示。

import pandas as pd 
import matplotlib.pyplot as plt 
df = pd.read_csv('https://raw.githubusercontent.com/parulnith/Website-articles-datasets/master/India%20GDP%20Growth%20Rate%20.csv', parse_dates=['Year']) 
df['Year'] = df['Year'].apply(lambda x: pd.Timestamp(x).strftime('%Y')) 
#calling xkcd() method 
plt.xkcd(scale=5, length=400) 
df.plot(x='Year',y='GDP Growth (%)',kind='bar') 
plt.ylabel('GDP Growth (%)') 
plt.xticks(rotation=-20) 
plt.figure(figsize=(10,8)) 
plt.show()

image

文章参考:
https://towardsdatascience.co...

先分享这些,如果觉得有帮助,还请多分享点个赞

欢迎大家关注我的原创微信公众号 Python数据科学,专注于写基于Python的数据算法、机器学习、深度学习硬核干货。

查看原文

Python数据科学 发布了文章 · 9月29日

安利 5 个拍案叫绝的 Matplotlib 骚操作!

公众号:Python数据科学
作者:东哥起飞

大家都知道,Matplotlib是Python的可视化库,功能很强,可以绘制各种图。一些常规用法前不久分享过Matplotlib官方出品的cheatsheet:Matplotlib官方小抄手册公开,配套可视化代码已打包!

但是!今天我们不走寻常路,专挑几个贼骚的操作分享下.

1. Span Selector

Span SelectorMatplotlib中的鼠标小部件,widgets是用于包含一些交互功能的python对象。Span Selector可以通过鼠标框选,方便地查看选定区域的最大值和最小值。

下面是代码,首先创建一个基本折线图作为例子。然后,我们调用SpanSelector方法并使用它来选择一个区域,然后在该区域中显示最大值和最小值。

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
def onselect(xmin, xmax):
    print(xmin, xmax)
    return xmin, xmax
fig, ax = plt.subplots()
ax.plot([1,2,3,4,5,6,7], [10, 50, 100, 23,15,28,45])
span = SpanSelector(ax, onselect, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red'))       
plt.show()

下面是具体操作。
image

2. Broken Barh

Broken的水平条形图是不连续具有间隙的图,它可用于数据值相差很大的情况下,例如,包含极端温度范围的数据集。在这种情况下,Broken的水平条形图非常合适,因为它们可以同时绘制最大和最小范围。

python模块matplotlib.broken_barh()用于绘制Broken的水平条形图。

import matplotlib.pyplot as plt 
#Defining the x and y ranges 
xranges = [(5,5), (20,5),(20,7)] 
yrange = (2,1) 
#Plotting the broken bar chart 
plt.broken_barh(xranges, yrange, facecolors='green') 
xranges = [(6,2), (17,5),(50,2)] 
yrange = (15,1) 
plt.broken_barh(xranges, yrange, facecolors='orange') 
xranges = [(5,2), (28,5),(40,2)] 
yrange = (30,1) 
plt.broken_barh(xranges, yrange, facecolors='red') 
plt.xlabel('Sales') 
plt.ylabel('Days of the Month') 
plt.show()

image

3. Table Demo

Matplotlib的表格功能也是可以在图中显示表格的。当我们希望以条形图的形式快速查看表格中的值时,这特别方便。表格可以放置在图表的顶部,底部或侧面。

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
x = np.random.rand(5, 8)*.7 
plt.plot(x.mean(axis=0), '-o', label='average per column') 
plt.xticks([]) 
plt.table(cellText=[['%1.2f' % xxx for xxx in xx] for xx in x],cellColours=plt.cm.GnBu(x),loc='bottom') 
plt.show()

image
image

4. Watermark Images

有时候我们觉得可视化的背景太单调了,想增加点趣味性,比如将与数据相关的图像作为水印覆盖到可视化图形上。下面就以NBA的詹皇为例试试水,最后呈现出詹皇的数据,同时背景是詹皇本人。

首先,导入要用的数据集,图片和必要的库pandas

import numpy as np 
import matplotlib.image as image 
import matplotlib.pyplot as plt 
import pandas as pd 
df = pd.read_csv('income.csv') 
im = image.imread('Lebron_James.jpeg') # Image

pandas过滤掉仅由勒布朗组成的数据。

lebron_james = df[df['Name']=='LeBron James']

然后像下面这样操作,使用figimage添加水印就ok了。

fig, ax = plt.subplots() 
ax.grid() 
ax.plot('Year','earnings ($ million)',data=lebron_james) 
ax.set_title("LeBron James earnings in US$(millions)") 
fig.figimage(im, 60, 40,cmap='ocean', alpha=.2) 
plt.show()

image

5. XKCD Plots

下面这个操作更有趣味性(更骚)。

如果你想让Matplotlib图上添加一些扭曲,可以简单地xkcd()pyplot对象上调用方法,如下所示。

import pandas as pd 
import matplotlib.pyplot as plt 
df = pd.read_csv('https://raw.githubusercontent.com/parulnith/Website-articles-datasets/master/India%20GDP%20Growth%20Rate%20.csv', parse_dates=['Year']) 
df['Year'] = df['Year'].apply(lambda x: pd.Timestamp(x).strftime('%Y')) 
#calling xkcd() method 
plt.xkcd(scale=5, length=400) 
df.plot(x='Year',y='GDP Growth (%)',kind='bar') 
plt.ylabel('GDP Growth (%)') 
plt.xticks(rotation=-20) 
plt.figure(figsize=(10,8)) 
plt.show()

image

文章参考:
https://towardsdatascience.co...

先分享这些,如果觉得有帮助,还请多分享点个赞

欢迎大家关注我的原创微信公众号 Python数据科学,专注于写基于Python的数据算法、机器学习、深度学习硬核干货。

查看原文

赞 5 收藏 3 评论 0

Python数据科学 发布了文章 · 6月19日

从机械转行数据科学,吐血整理了这些白嫖的学习网站

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

大家好,我是东哥。

前方高能,准备开启收藏夹吃灰模式。

本篇东哥分享几个数据科学入门的学习网站,全部免费资源,且内容优质,是小白入门的不二选择。东哥当年从机械转行也从这些学习网站收获很多。

下面开始进入正题。

一、Kaggle

什么是Kaggle?

在这里插入图片描述
kaggle是全球最先也是目前规模最大的数据科学竞赛组织了。之所以这么受欢迎,是因为很多大的公司奉献出自家真实的数据给kaggle,提出真实业务场景面临的痛点,需要数据科学上的解决方案。

我个人觉得是非常有意思的,因为很多人苦学理论,正愁着没有真实数据去实践,有了这个机会,不论方案是否可以排上名次,都是宝贵的实践经验啊。

当然,作为方案最优的前三名可以得到一笔丰厚的报酬,几千美元到几万美元不等,这更加刺激广大数据爱好者了。竞赛已经有上百场了,各种场景和需求,并且随着需求增多,竞赛也在不断增加。

Kaggle上有什么?

原来的kaggle只有单一的竞赛,现在的kaggle已经不只是竞赛这么简单了。它还有丰富的社区免费的学习课程在线实操的环境

下面是一在线操作提交模型的环境,非常奈斯。

在这里插入图片描述
各路神仙在社区共享自己的kernels和源代码,是个非常好的交流学习机会,有兴趣可以自己去看。这里主要说下免费的学习课程,下面是地址。

https://www.kaggle.com/learn/...

在这里插入图片描述

学习列表中有python、机器学习、深度学习、可视化、pandas数据处理、SqL入门和进阶等等。打开每个学习项目,内容非常丰富,虽是英文我相信对于有心的人都不是问题了。

二、Coursera

在这里插入图片描述
Coursera很多朋友应该熟悉,吴恩达的机器学习课程最早就是从这里开始分享的。里面有各个名校大学的公开课,很多都是免费公开的课程,听课是免费的,但学完后认证证书需要付费。

这里分享几个东哥收藏的宝藏课程,每一个都很经典,好评无数。

1.机器学习 (Andrew Ng / 斯坦福大学)

在这里插入图片描述

https://www.coursera.org/lear...

2.专业数据科学(10门课/JHU)

在这里插入图片描述

https://www.coursera.org/spec...

3.数据科学实战(5门课/JHU)

在这里插入图片描述

https://www.coursera.org/spec...

4.专业数据挖掘(6门课/伊利诺伊大学)

在这里插入图片描述

https://www.coursera.org/spec...

5.数据科学硕士(8门课/伊利诺伊大学圣巴巴拉分校)

在这里插入图片描述

https://www.coursera.org/degr...

6.数据科学应用硕士(密歇根大学)

在这里插入图片描述

https://www.coursera.org/degr...

三、Udacity

Udacity(优达学成)是个美国的付费类培训机构,内容涉及所有编程和计算机类的课程,是歪果仁拍的视频课程,质量很高,但收费很贵。

仔细观察,其实也有很多免费的课程供学习的,东哥把收藏的存货也拿出来分享下。

1.数据科学导论

总共10个章节,以titanicNewYork Subway data项目为例介绍数据分析、可视化、数据处理、Mapreduce大数据。
在这里插入图片描述

https://www.udacity.com/cours...

2.数据分析导论

在这里插入图片描述

https://www.udacity.com/cours...

3.数据可视化分析

这个课程是基于R语言的,介绍了R语言基础、逻辑回归、线性回归、正则化等内容。
在这里插入图片描述

https://www.udacity.com/cours...

4.使用SQL做数据分析

在这里插入图片描述

https://www.udacity.com/cours...

5. 统计推理入门

主要介绍推断性统计的知识,比如各种检验,假设检验、t检验、卡方检验、ANOVA方差分析、回归等等。

在这里插入图片描述

https://www.udacity.com/cours...

当然,除了这些还有很多付费的,感兴趣可自行查找,本篇只谈免费。

四、其它社区和博客

下面是几个很好的国外数据科学社区和个人博客,内容不如前面三个学习网站有组织和条条理,但是有很多优秀的文章分享也可以作为参考学习。

1. 面向数据科学

https://towardsdatascience.com/

这里着重说下这个社区,专门的数据科学学习平台,里面都是一些国外爱好者的分享,涵盖了data sciencemachine learningdeep learningvisualizationprogramming等,缺点是需要特殊工具才能上去,用谷歌访问助手也可以。

2. 方差解释

http://varianceexplained.org/

3. 成为一名数据科学家

https://www.becomingadatascie...

4. Mark Meloon

https://www.markmeloon.com/

5. Julia Silge

https://juliasilge.com/blog/

以上就是东哥分享的一些免费课程资源,资源多少不是关键,关键的是迈开第一步,深入进去开始学习。

先分享这些,如果觉得有帮助,还请多分享点个赞

欢迎大家关注我的原创微信公众号 Python数据科学,专注于写基于Python的数据算法、机器学习、深度学习硬核干货。

查看原文

赞 6 收藏 3 评论 0

Python数据科学 关注了专栏 · 6月18日

AWS_AI开发社区

AWS_AI 开发者社区是专注于人工智能领域 IT 人士交流与互动的平台。在这里,你可以分享和获取一切有关人工智能的相关技术和前沿知识,也可以与同行或爱好者们交流探讨,共同成长。

关注 629

Python数据科学 发布了文章 · 6月14日

太香了!墙裂推荐6个Python数据分析神器!!

作者:东哥起飞,数据爱好者
Python数据科学

hello,大家好我是东哥!

用Python处理数据大家都不陌生了,属常规操作,但常规之下还是也有些暗藏技巧的,本篇东哥分享6个好玩高效的操作,帮助大家提高效率。

一、Pandas Profiling

Pandas Profiling提供数据的一个整体报告,是一个帮助我们理解数据的过程。它可以简单快速地对Pandas的数据框数据进行探索性数据分析。

其实,Pandasdf.describe()df.info()函数也可以实现数据探索过程第一步。但它们只提供了对数据非常基本的概述。而Pandas中的Profiling功能简单通过一行代码就能显示大量信息,同时还能生成交互式HTML报告。

对于给定的数据集,Pandas中的profiling包计算了以下统计信息:

image.png

Pandas Profiling包计算出的统计信息包括直方图、众数、相关系数、分位数、描述统计量、其他信息包括类型、单一变量值、缺失值等。

安装

pipconda即可,使用方法很简单,如下:

pip install pandas-profiling
conda install -c anaconda pandas-profiling

用法

以titanic数据集来演示profiling的功能。

import pandas as pd
import pandas_profiling
df = pd.read_csv('titanic/train.csv')
pandas_profiling.ProfileReport(df) 

在这里插入图片描述
除了导入库之外只需要一行代码,就能显示数据报告的详细信息,包括必要的图表。

还可以使用以下代码将报告导出到交互式HTML文件中。

profile = pandas_profiling.ProfileReport(df)
profile.to_file(outputfile="Titanic data profiling.html")

在这里插入图片描述

二、pretty print

pprint是Python中的内置模块。它能够以格式清晰,可读性强漂亮格式打印任意数据结构。一个例子对比下printpprint

# 定义个字典,测试用
my_dict = {'Student_ID': 34,'Student_name' : 'Tom', 'Student_class' : 5,
          'Student_marks' : {'maths' : 92,
                            'science' : 95,
                            'social_science' : 65,
                            'English' : 88}
          }

print

# 正常的print
print(my_dict)
# 输出结果如下:
{'Student_ID': 34, 'Student_name': 'Tom', 'Student_class': 5, 'Student_marks': {'maths': 92, 'science': 95, 'social_science': 65, 'English': 88}}

pprint

# 使用pprint输出
import pprint
pprint.pprint(my_dict)
# 输出结果如下:
{'Student_ID': 34,
 'Student_class': 5,
 'Student_marks': {'English': 88,
                   'maths': 92,
                   'science': 95,
                   'social_science': 65},
 'Student_name': 'Tom'}

可以清楚看到pprint的优势之处,数据结构一目了然啊。

三、Python Debugger

交互式调试器也是一个神奇的函数,如果在运行代码单元格时出现报错,可以在新行中键入%debug运行它。这将打开一个交互式调试环境,自动转到报错发生的位置,并且还可以检查程序中分配的变量值并执行操作。要退出调试器,按q。比如下面这个例子。

x = [1,2,3]
y = 2
z = 5

result = y+z
print(result)
result2 = x+y
print(result2)

大家应该能看出x+y肯定会报错,因为二者不是一个类型,无法进行运算操作。然后我们敲入%debug

%debug

这时会出现对话框让我们互交式输入命令,比如我们可以像下面这样做。

在这里插入图片描述

四、Cufflinks

这个东哥在之前也介绍过,对于数据探索的可视化分析超级好用,低代码量便可生成漂亮的可视化图形。下面举一个例子,详细的可参见这篇Python一行代码搞定炫酷可视化,你需要了解一下Cufflinks

cufflinksplotly的基础上做了一进一步的包装,方法统一,参数配置简单。其次它还可以结合pandasdataframe随意灵活地画图。可以把它形容为pandas like visualization

比如下面的lins线图

import pandas as pd
import cufflinks as cf
import numpy as np

cf.set_config_file(offline=True)
cf.datagen.lines(1,500).ta_plot(study='sma',periods=[13,21,55])

在这里插入图片描述
再比如box箱型图

cf.datagen.box(20).iplot(kind='box',legend=False)

在这里插入图片描述

五、Pyforest

这是一个能让你偷懒的import神器,可以提前在配置文件里写好要导入的三方库,这样每次编辑脚本的时候就省去了开头的一大堆import 各种库,对于有常用和固定使用库的朋友来说无疑也是提高效率的工具之一。

pyforest支持大部分流行的数据科学库,比如pandasnumpymatplotlibseabornsklearntensorflow等等,以及常用的辅助库如ossysrepickle等。

此用法对于自己频繁调试很方便,但对于那些频繁跨环境比如和其它人共享脚本调试的时候就不是很好用了,因为别人不一定使用它。

此库东哥在之前也详细介绍过 牛逼!这个Python库竟然可以偷懒,和import说再见!看下面这个操作就明白了。

在这里插入图片描述

六、Jupyter notebook的笔记高亮

此方法仅适用于Jupyter notebook中,当我们想高亮笔记,让笔记变得美观的时候,这个方法非常的香。

笔记的高亮的颜色根据不同情况分为几种,前端的同学一看就明白,区别就是每种颜色代码的class类型不一样,其它只要在div标签中写内容就好。下面看下用法。

蓝色代表info

<div class="alert alert-block alert-info">
<b>Tip:</b> Use blue boxes (alert-info) for tips and notes. 
If it’s a note, you don’t have to include the word “Note”.
</div>

在这里插入图片描述
黄色代表warning

<div class="alert alert-block alert-warning">
<b>Example:</b> Yellow Boxes are generally used to include additional examples or mathematical formulas.
</div>

在这里插入图片描述
绿色代表success

<div class="alert alert-block alert-success">
Use green box only when necessary like to display links to related content.
</div>

在这里插入图片描述
红色代表danger

<div class="alert alert-block alert-danger">
It is good to avoid red boxes but can be used to alert users to not delete some important part of code etc. 
</div>

在这里插入图片描述
这里有个小提示东哥提示下,如果你直接复制到jupyter notebook中可能会报错,因为默认是代码的格式,所以你需要选中单元格按Esc变成可切换模式,然后再按Y切换成文本模式。这时候再运行shift+ok就ok了。看下面这个例子。

在这里插入图片描述
以上就是本次分享内容,欢迎各位朋友点赞留言收藏。

欢迎大家关注我的原创微信公众号 Python数据科学

查看原文

赞 6 收藏 3 评论 0

Python数据科学 发布了文章 · 6月7日

太赞了!分享一个数据科学利器 PyCaret,几行代码搞定从数据处理到模型部署

作者:帅阿东,数据爱好者
Python数据科学出品

学习数据科学很久了,从数据探索、数据预处理、数据模型搭建和部署这些过程一直有些重复性的工作比较浪费时间,尤其当你有个新的想法想要快速尝试下效果的时候,效率很低。

东哥最近发现一个开源的Python机器学习库,名字叫PyCaret,这个轮子正好可以为了解决我刚才所描述的困扰,它的特点是以low-code低代码量来快速解决从数据预处理到模型部署的整个流程。
在这里插入图片描述
用了一下感觉确实有点香,因此也和大家分享一下。

PyCaret是什么?

PyCaret是一个将我们常用到的机器学习库进行封装了的轮子。

常用的都有啥呢?

比如pandas,numpy做数据处理的,matplotlib,seaborn数据可视化的,sklearn,xgboost,catboost,lightgbm等各种模型的,总共有30个。在安装PyCaret的时候会附带着一起都安装上。

封装这么多库干什么用?

PyCaret依赖了这么多的神库肯定是要搞事情啊。没错,机器学习中的一些操作步骤都可在PyCaret自动开发的pipeline中进行复现。在 Pycaret 中所执行的所有操作均按顺序存储在 Pipeline 中,该 Pipeline 针对模型部署进行了完全配置。

PyCaret就像是把所有都安排好了一样,我们按照它定义的函数使用就可以了。不管是填充缺失值、转换类别数据、执行特征工程设计,还是调参,Pycaret 都能够自动执行。 所以才可以实现用几行代码搞定从预处理到模型部署的整个流程。

而且pipeline可以保存为二进制文件格式,支持在不同环境中进行迁移。

PyCaret支持的模型算法

PyCaret支持6个模块,有监督无监督模型的训练和部署,分别有分类、回归、聚类、异常检测、自然语言处理和关联规则挖掘。
在这里插入图片描述

PyCaret安装

pip install pycaret

老样子,命令行pip install皆可安装。

为了防止安装的这些依赖可能与之前你已安装过的发生冲突,建议可以创建个Python的虚拟环境安装PyCaret以减少不必要的麻烦,比如用python3 virtualenv或者conda。就拿conda为例吧。

#创建一个新的虚拟环境
conda create --name yourenvname python=3.7
#激活
conda activate yourenvname
#安装
pip install pycaret

如果不好使也可以尝试从源安装。

pip install C:/path_to_download/pycaret-version.tar.gz

PyCaret如何使用?

像这种数据建模类的工作会涉及很多交互式的操作,所以东哥首推在Jupyter notebook中运行代码。

PyCaret库的函数有五个大类,初始化、模型训练、模型集成、模型分析与模型部署,基本上覆盖了我们正常建模的顺序,只不过预处理都在初始化中完成了。具体使用方法见后面实例。

一、初始化

PyCaret初始化包括了两部分内容,一、获取数据;二、建立环境。

1. 获取数据

PyCaret自带了很多数据集,样本几万条的,特征几百个的,对于我们练习绝对是够用了。比如这样:

from pycaret.datasets import get_data
data = get_data('juice') 

2. 建立环境

这一步是必须的。首先,我们要选择使用哪个模块,分类、回归、聚类
还是其他的。比如我们要用classification分类模型。

from pycaret.datasets import get_data
diabetes = get_data('diabetes')
# 初始化
from pycaret.classification import *
clf1 = setup(data = diabetes, target = 'Class variable')

上面setup函数就建立了基础环境,其中参数约束了数据集和目标变量。
在这里插入图片描述

setup参数除了上面这两个以外,还有N多个参数可以控制。所有预处理的步骤都会应用至 setup() 中,PyCaret 拥有 20 余项功能可运用于 ML 相关的数据准备,比如样本的划分数据预处理缺失值处理独热编码归一化特征工程特征选择等等。
在这里插入图片描述
比如要用归一化,那么令normalizeTure就好了,其它的同理。

clf1 = setup(data = pokemon, target = 'Legendary', normalize = True)

如果还要用其他的,在setup里面加就好了,至于处理的顺序不用我们管,pipeline已经自动搞定了。

另外,PyCaret 的一大优点是: Pipeline 可保存成二进制,轻松地在各环境之间相互迁移,比如大规模运行或是轻松部署到生产环境中。

二、模型训练

模型训练包括三个部分,模型比较,模型创建,模型调优

1. 模型比较

这是模型训练的第一步。compare_models 函数会训练模型库中的所有模型,并使用 k 折交叉验证(默认 k=10)来比较常见的评估指标。所使用的评估指标如下所示:

  • 分类模块:Accuracy, AUC, Recall, Precision, F1, Kappa
  • 回归模块:MAE, MSE, RMSE, R2, RMSLE, MAPE

下面是模型比较函数的使用,只需要这么一行代码!

# 比较所有模型
compare_models()

来看一下结果,直接给出所有模型跑出的结果,直观地对比。
在这里插入图片描述

2. 模型创建

当我们比较了各模型的结果后,知道了哪个模型最适合,这时只要在创建函数create_model中传入一个模型参数就行,同样一行代码搞定。

# 创建逻辑回归模型
lr = create_model('lr')

在这里插入图片描述
PyCaret 有 60 多个开源即用型算法,每个模型都有对应的缩写(可以查表),比如上面逻辑回归直接写上lr就可以完成。

变量lr存储一个由create_model函数返回的训练模型对象,可以通过在变量后使用标点.来访问训练对象的原始属性。

3. 模型调优

同样的,在模型调优tune_model函数中传入模型lr参数,PyCaret将自动调优。

# 调节 LR 模型
tuned_lr = tune_model('lr')

在这里插入图片描述

三、模型集成

1. 集成模型
模型集成函数ensemble_model可以直接调用生成的模型对象,然后做集成处理。默认使用Bagging方法用于模型集成,用户也可函数中的method参数将其转换为Boosting

# 创建一个决策树模型
dt = create_model('dt')
dt_bagged = ensemble_model(dt)

在这里插入图片描述
除此外,PyCaret还提供了blend_modelsstack_models 功能,来集成多个训练好的模型。

2. blend模型

# blend_models 混合特殊的模型
blender = blend_models(estimator_list = [dt, catboost, lightgbm])

3. stack模型

# 创建单个模型,用于stacking
ridge = create_model('ridge')
lda = create_model('lda')
gbc = create_model('gbc')
xgboost = create_model('xgboost')
# stacking 模型
stacker = stack_models(estimator_list = [ridge,lda,gbc], meta_model = xgboost)

四、模型分析

模型分析主要可以做两个事情:一、模型绘制;二、模型解释。

1. 模型绘制

我们需要分析什么模型指标,只要传入函数中即可,比如对adaboost模型分析AUC指标。

# 创建逻辑回归模型
adaboost = create_model('adaboost') 
plot_model(adaboost, plot = 'auc') # AUC plot
plot_model(adaboost, plot = 'boundary') # Decision Boundary
plot_model(adaboost, plot = 'pr') # Precision Recall Curve
plot_model(adaboost, plot = 'vc') # Validation Curve

在这里插入图片描述
如果你不想单独绘制所有这些可视化,那么PyCaret库有另一个惊人的功能evaluate_model。在此功能中,只需要传递模型对象,PyCaret将创建一个交互式窗口,供你·以所有可能的方式查看和分析模型:
在这里插入图片描述

2. 模型解释

在大多数机器学习项目中,解释复杂模型非常重要。通过分析模型认为重要的内容,有助于模型调优。在PyCaret中,此步骤非常简单,只需编写interpret_model即可获取Shapley值。

# 创建一个模型
xgboost = create_model('xgboost')
interpret_model(xgboost) # summary plot
interpret_model(xgboost, plot = 'correlation') # correlation plot

在这里插入图片描述
测试数据集上特定数据点的解释可以通过reason图来评估。如下图所示:在测试数据集上检查首个实例。

interpret_model(xgboost, plot = 'reason', observation = 0)

在这里插入图片描述

五、模型部署

模型调优后要将模型在测试集上进行测试,使用predict_model函数。

1. 模型预测

# 创建模型
rf = create_model('rf') # 预测测试集
rf_holdout_pred = predict_model(rf)

在这里插入图片描述
以上是对模型测试集进行的预测,如果对于未见过的新数据预测,PyCaret提供一个迭代的预测结果,在predict_model函数指定data,像下面这样。

2. 模型完成

最后确认模型finalize_model才能进行部署。

# finalize a model
final_rf = finalize_model(rf)

3. 模型部署

该功能将pipeline和经过训练的模型保存为最终用户应用程序可以作为二进制pickle文件使用。或者,可以使用PyCaret将模型部署在云上。在云上部署模型就像编写deploy_model一样简单。

比如对于AWS用户来说,在将模型部署到AWS S3('aws')之前,必须使用命令行界面配置环境变量。要配置AWS环境变量,请在python命令行中输入aws configure。需要以下信息,可以使用亚马逊控制台帐户的身份和访问管理(IAM)门户生成。

  • AWS访问密钥ID
  • AWS访问密钥
  • 默认区域名称(可以在您的AWS控制台的“全局设置”下看到)
  • 默认输出格式(必须留空)
# 创建模型
lr = create_model('lr')
# 最终确定模型
final_lr = finalize_model(lr)
# 部署模型
deploy_model(final_lr, model_name = 'lr_aws', platform = 'aws', authentication = { 'bucket'  : 'pycaret-test' })

用户也能够以二进制文件的格式保存整个实验,包括所有中间输出。

# 创建模型
adaboost = create_model('ada') 
# 二进制保存模型
save_model(adaboost, model_name = 'ada_for_deployment') 

以上就是PyCaret的介绍和使用方法,具体教程也可以参考:

https://pycaret.org/guide/

老铁,要不点个赞再走可好?么么哒

关注我的原创微信公众号 Python数据科学,专注于写基于Python的数据算法、机器学习、深度学习硬核干货。

保证让你看完有所收获,不信你打我。后台回复『干货』送你Python入门、机器学习、数据挖掘等丰富项目资源。

作者简介
作者:大家好,我是帅阿东。原为机械专业,凭借自己的努力成功转行数据分析,目前担任某大银行风控建模职位,创立『Python数据科学』公众号,拥有近10w粉丝,文章涵盖爬虫,数据分析、机器学习等大量干货和实战项目讲解,提供海量学习资源,期待你的关注,和我一起学习。 转载说明:未获得授权,禁止转载。
查看原文

赞 7 收藏 5 评论 0

Python数据科学 收藏了文章 · 6月5日

深入理解 MySql 的 Explain

timg.jpg

相信大部分入门数据库的朋友都是从数据库的“增删改查”学起的。其实,对于很多搞业务的非专业技术人员而言,可能基本的增删改查也够用了,因为目的并不是要写的多好,只要能正确查到自己想要的分析的数据就可以了。

但是,对于一个专业搞数据分析的人而言,可就没那么简单了。这个自己平时跑个小数可能也没啥感觉,但现实工作中当公司业务数据量达到百万甚至千万级以上时,一个查询语句写的好坏所造成的影响就尤为明显了。所以也就不难理解为什么面试的时候面试官喜欢问一些关于优化的问题。

为了了解自己写的SQL是好是坏,MySql提供了Explain执行计划功能。它对优化SQL语句尤为的重要,通过它可以看清执行过程的细节,分析查询语句或是结构的性能瓶颈,找到问题所在。

如何使用Explain?

explain的使用很简单,就是在select 语句之前增加 explain关键字就ok了。MySQL 会在查询上设置一个标记,执行查询时,会返回执行计划的信息,而不是执行这条SQL。比如这样:

# explain + sql
explain select * from table where a = 1;

Explain执行计划能做什么?

  • 确定表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

可以看出执行计划给我们提供的信息是非常有帮助的。只有读懂了这些内容,才能定位问题点在哪,进而去解决。下面东哥给大家介绍一下explain执行计划的内容。

因为有些字段光看很难理解,因此建立三个表作为例子来说明,感兴趣的朋友也可以自己跑下试试。

DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (
 `id` int(11) NOT NULL,
 `name` varchar(45) DEFAULT NULL,
 `update_time` datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1,'a','2017-12-22 15:27:18');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (2,'b','2017-12-22 15:27:18');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (3,'c','2017-12-22 15:27:18');
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(10) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film` (`id`, `name`) VALUES (3,'film0');
INSERT INTO `film` (`id`, `name`) VALUES (1,'film1');
INSERT INTO `film` (`id`, `name`) VALUES (2,'film2');
DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
 `id` int(11) NOT NULL,
 `film_id` int(11) NOT NULL,
 `actor_id` int(11) NOT NULL,
 `remark` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (2,1,2);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (3,2,1);

注意:上面三张表中,actor主键为id;film主键为id,以name字段为索引;film_actor表中id为主键,以film_id和actor_id为联合索引。

执行计划的内容介绍

我们在Navicat里随便执行一个查询语句,看看都会返回哪些内容。

explain select (select id from actor limit 1) from film;

image.png
执行后的结果不是查询的数据而是执行计划的解释,一共有id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra这些字段,每个都代表不同的含义,下面详细介绍。

id

id 决定了每个表的加载和读取顺序。比如你写了个复杂的嵌套逻辑,有很多子查询,那每个select执行的顺序就可通过id序列号观察出来。

原则是:id值越大越先被执行。id值相同的按从上到下的顺序执行。id为NULL的最后执行。

1、id相同

explain select * from film, actor, film_actor where film.id=actor.id and film.id=film_actor.id;

image.png

2、id不同

explain select (select id from actor limit 1) from film;

image.png

select_type

select查询的类型主要有三大类:

1、简单类型

SIMPLE:最简单的select查询,就是查询中不包含子查询或者union,表里如一。

explain select * from film where id=1;

image.png

2、嵌套类型

PRIMARY、SUBQUERY、DERIVED 这三个是用在有嵌套逻辑的语句中的。

PRIMARY:嵌套查询最外层的部分被标记为PRIMARY。

SUBQUERY:出现在select或者where后面中的子查询被标记为SUBQUERY。

DERIVED:这个其实我理解是SUBQUERY的一种特例,只不过出现的位置比较特殊,是在from后面的子查询,MySQL会将子查询结果存放在一个临时表中,称为派生表,因为这是我们派生出来的,而非原始表。

通过一个例子说明。

explain select (select id from actor where id = 1) from (select * from film) t;

image.png

3、组合类型

组合类型包括UNION和UNION RESULT两个。

UNION:UNION前后如果有两个select ,那么把出现在union之后的第二个select标记为UNION;如果UNION包含在from 子句的子查询中,外层select将被标记为DERIVED。

UNION RESULT:从 UNION表获取结果的select。

通过一个例子说明。

explain select id from actor union all select id from actor;

image.png

table

表示正在访问哪个表,以表的名称出现。

但是有两个特殊的情况:

1)当 from 子句中有子查询(派生表)时,那table就会以 < derivedN > 格式出现。因为此时查询所依赖的表是一个我们派生出来的表,即依赖一个 id 为 N 的子查询的。比如:

explain select (select id from actor where id = 1) from (select * from film) t;

image.png
2)当使用 union 时,UNION RESULT 的 table 值为 <union1,2>,1和2表示参与 union 的 select 行id。比如:

explain select id from actor union all select id from actor;

image.png

type

访问类型,表示MySQL是如何访问数据的,是全表扫描还是通过索引等?这是考量sql查询优化中一个很重要的指标,共分有很多种类型,结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,好的sql查询至少达到range级别,最好能达到ref。下面挑几个常见且比较重要的说一下。

1. system

表里只有一行记录,这个属于const类型的特例,一行数据平时很少出现,可以忽略不计。

2. const

表示通过索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const。

systemconst有啥区别呢?看解释不太好理解,举一个例子。

explain select * from (select * from film where id = 1) tmp;

image.png
这里子查询就是const,而最外层查询则为system,为什么呢?

因为子查询将主键id置于where中选择,我们知道主键是有唯一性的,所以这个子查询就只返回一行记录,即匹配了一行数据。而外层查询没得选,因为子查询派生表就给了它一行数据,也就是说它要查询的表里就一行数据。因此,system是表里只有一行数据,const是从表里选出唯一一条数据,表里可能很多数据。

3. eq_ref

唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描。

explain select * from film_actor left join film on film_actor.film_id = film.id;

image.png

4. ref

相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。举例如下:

普通索引的简单查询

explain select * from film where name = "film1";

image.png
关联表查询,idx_film_actor_idfilm_idactor_id的联合索引。这里使用到了film_actor的左边前缀film_id部分。

explain select film_id from film left join film_actor on film.id = film_actor.film_id;

image.png

5. range

只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween<>in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引

explain select * from actor where id > 1;

image.png

6. index

Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)

explain select * from film;

image.png
这里用了查找所有*,但也返回了index,这是因为这个表里的两个字段都是索引,id是主键,name也被定位为索引。

7. all

全表扫描,意味MySQL需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了。

explain select * from film_actor;

image.png

possible_keys

这一列显示查询可能使用哪些索引来查找。explain 时可能出现 possible_keys 有列,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。

如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提高查询性能,然后用 explain 查看效果。

key

这一列显示MySQL实际采用哪个索引来优化对该表的访问。如果没有使用索引,则该列是 NULL。如果想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index。

key_len

表示索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。key_len是根据表定义计算而得的,不是通过表内检索出的

举例说明:film_actor的联合索引 idx_film_actor_idfilm_idactor_id 两个int列组成,并且每个int是4字节。通过结果中的key_len=4可推断出查询使用了第一个列:film_id列来执行索引查找。

explain select * from film_actor where film_id = 2;

image.png

ref

这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名。举例如下:

ref为常量

explain select * from film_actor where film_id = 2;

image.png

ref为字段

explain select film_id from film left join film_actor on film.id = film_actor.film_id;

image.png

rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

Extra

最后一列展示额外的信息。有以下几种重要的值,Using filesort Using temporaryUsing indexUsing where Using index,``

1、Using filesort

MySQL对数据使用一个外部的索引排序,而不是按照表内的索引进行排序读取。也就是说mysql无法利用索引完成的排序操作成为“文件排序” 。这种情况下一般也是要考虑使用索引来优化的。

explain select * from actor order by name;

image.png

2、Using temporary

mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。常见于order by 和 group by。

举例如下:
actor.name没有索引,此时创建了张临时表。

explain select distinct name from actor;

image.png

3、Using index

表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高
如果同时出现Using where,表明索引被用来执行索引键值的查找
如果没用同时出现Using where,表明索引用来读取数据而非执行查找动作。

explain select film_id from film_actor where film_id = 1;

image.png

关于索引会专门写一篇文章介绍。

参考:

https://blog.csdn.net/belalds...
https://blog.csdn.net/UncleMo...

最后,如果喜欢本篇文章,欢迎点赞和收藏。
更多精彩内容请关注我的微信公众号:Python数据科学

查看原文

Python数据科学 发布了文章 · 6月5日

深入理解 MySql 的 Explain

timg.jpg

相信大部分入门数据库的朋友都是从数据库的“增删改查”学起的。其实,对于很多搞业务的非专业技术人员而言,可能基本的增删改查也够用了,因为目的并不是要写的多好,只要能正确查到自己想要的分析的数据就可以了。

但是,对于一个专业搞数据分析的人而言,可就没那么简单了。这个自己平时跑个小数可能也没啥感觉,但现实工作中当公司业务数据量达到百万甚至千万级以上时,一个查询语句写的好坏所造成的影响就尤为明显了。所以也就不难理解为什么面试的时候面试官喜欢问一些关于优化的问题。

为了了解自己写的SQL是好是坏,MySql提供了Explain执行计划功能。它对优化SQL语句尤为的重要,通过它可以看清执行过程的细节,分析查询语句或是结构的性能瓶颈,找到问题所在。

如何使用Explain?

explain的使用很简单,就是在select 语句之前增加 explain关键字就ok了。MySQL 会在查询上设置一个标记,执行查询时,会返回执行计划的信息,而不是执行这条SQL。比如这样:

# explain + sql
explain select * from table where a = 1;

Explain执行计划能做什么?

  • 确定表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

可以看出执行计划给我们提供的信息是非常有帮助的。只有读懂了这些内容,才能定位问题点在哪,进而去解决。下面东哥给大家介绍一下explain执行计划的内容。

因为有些字段光看很难理解,因此建立三个表作为例子来说明,感兴趣的朋友也可以自己跑下试试。

DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (
 `id` int(11) NOT NULL,
 `name` varchar(45) DEFAULT NULL,
 `update_time` datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1,'a','2017-12-22 15:27:18');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (2,'b','2017-12-22 15:27:18');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (3,'c','2017-12-22 15:27:18');
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(10) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film` (`id`, `name`) VALUES (3,'film0');
INSERT INTO `film` (`id`, `name`) VALUES (1,'film1');
INSERT INTO `film` (`id`, `name`) VALUES (2,'film2');
DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
 `id` int(11) NOT NULL,
 `film_id` int(11) NOT NULL,
 `actor_id` int(11) NOT NULL,
 `remark` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (2,1,2);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (3,2,1);

注意:上面三张表中,actor主键为id;film主键为id,以name字段为索引;film_actor表中id为主键,以film_id和actor_id为联合索引。

执行计划的内容介绍

我们在Navicat里随便执行一个查询语句,看看都会返回哪些内容。

explain select (select id from actor limit 1) from film;

image.png
执行后的结果不是查询的数据而是执行计划的解释,一共有id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra这些字段,每个都代表不同的含义,下面详细介绍。

id

id 决定了每个表的加载和读取顺序。比如你写了个复杂的嵌套逻辑,有很多子查询,那每个select执行的顺序就可通过id序列号观察出来。

原则是:id值越大越先被执行。id值相同的按从上到下的顺序执行。id为NULL的最后执行。

1、id相同

explain select * from film, actor, film_actor where film.id=actor.id and film.id=film_actor.id;

image.png

2、id不同

explain select (select id from actor limit 1) from film;

image.png

select_type

select查询的类型主要有三大类:

1、简单类型

SIMPLE:最简单的select查询,就是查询中不包含子查询或者union,表里如一。

explain select * from film where id=1;

image.png

2、嵌套类型

PRIMARY、SUBQUERY、DERIVED 这三个是用在有嵌套逻辑的语句中的。

PRIMARY:嵌套查询最外层的部分被标记为PRIMARY。

SUBQUERY:出现在select或者where后面中的子查询被标记为SUBQUERY。

DERIVED:这个其实我理解是SUBQUERY的一种特例,只不过出现的位置比较特殊,是在from后面的子查询,MySQL会将子查询结果存放在一个临时表中,称为派生表,因为这是我们派生出来的,而非原始表。

通过一个例子说明。

explain select (select id from actor where id = 1) from (select * from film) t;

image.png

3、组合类型

组合类型包括UNION和UNION RESULT两个。

UNION:UNION前后如果有两个select ,那么把出现在union之后的第二个select标记为UNION;如果UNION包含在from 子句的子查询中,外层select将被标记为DERIVED。

UNION RESULT:从 UNION表获取结果的select。

通过一个例子说明。

explain select id from actor union all select id from actor;

image.png

table

表示正在访问哪个表,以表的名称出现。

但是有两个特殊的情况:

1)当 from 子句中有子查询(派生表)时,那table就会以 < derivedN > 格式出现。因为此时查询所依赖的表是一个我们派生出来的表,即依赖一个 id 为 N 的子查询的。比如:

explain select (select id from actor where id = 1) from (select * from film) t;

image.png
2)当使用 union 时,UNION RESULT 的 table 值为 <union1,2>,1和2表示参与 union 的 select 行id。比如:

explain select id from actor union all select id from actor;

image.png

type

访问类型,表示MySQL是如何访问数据的,是全表扫描还是通过索引等?这是考量sql查询优化中一个很重要的指标,共分有很多种类型,结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,好的sql查询至少达到range级别,最好能达到ref。下面挑几个常见且比较重要的说一下。

1. system

表里只有一行记录,这个属于const类型的特例,一行数据平时很少出现,可以忽略不计。

2. const

表示通过索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const。

systemconst有啥区别呢?看解释不太好理解,举一个例子。

explain select * from (select * from film where id = 1) tmp;

image.png
这里子查询就是const,而最外层查询则为system,为什么呢?

因为子查询将主键id置于where中选择,我们知道主键是有唯一性的,所以这个子查询就只返回一行记录,即匹配了一行数据。而外层查询没得选,因为子查询派生表就给了它一行数据,也就是说它要查询的表里就一行数据。因此,system是表里只有一行数据,const是从表里选出唯一一条数据,表里可能很多数据。

3. eq_ref

唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描。

explain select * from film_actor left join film on film_actor.film_id = film.id;

image.png

4. ref

相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。举例如下:

普通索引的简单查询

explain select * from film where name = "film1";

image.png
关联表查询,idx_film_actor_idfilm_idactor_id的联合索引。这里使用到了film_actor的左边前缀film_id部分。

explain select film_id from film left join film_actor on film.id = film_actor.film_id;

image.png

5. range

只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween<>in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引

explain select * from actor where id > 1;

image.png

6. index

Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)

explain select * from film;

image.png
这里用了查找所有*,但也返回了index,这是因为这个表里的两个字段都是索引,id是主键,name也被定位为索引。

7. all

全表扫描,意味MySQL需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了。

explain select * from film_actor;

image.png

possible_keys

这一列显示查询可能使用哪些索引来查找。explain 时可能出现 possible_keys 有列,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。

如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提高查询性能,然后用 explain 查看效果。

key

这一列显示MySQL实际采用哪个索引来优化对该表的访问。如果没有使用索引,则该列是 NULL。如果想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index。

key_len

表示索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。key_len是根据表定义计算而得的,不是通过表内检索出的

举例说明:film_actor的联合索引 idx_film_actor_idfilm_idactor_id 两个int列组成,并且每个int是4字节。通过结果中的key_len=4可推断出查询使用了第一个列:film_id列来执行索引查找。

explain select * from film_actor where film_id = 2;

image.png

ref

这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名。举例如下:

ref为常量

explain select * from film_actor where film_id = 2;

image.png

ref为字段

explain select film_id from film left join film_actor on film.id = film_actor.film_id;

image.png

rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

Extra

最后一列展示额外的信息。有以下几种重要的值,Using filesort Using temporaryUsing indexUsing where Using index,``

1、Using filesort

MySQL对数据使用一个外部的索引排序,而不是按照表内的索引进行排序读取。也就是说mysql无法利用索引完成的排序操作成为“文件排序” 。这种情况下一般也是要考虑使用索引来优化的。

explain select * from actor order by name;

image.png

2、Using temporary

mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。常见于order by 和 group by。

举例如下:
actor.name没有索引,此时创建了张临时表。

explain select distinct name from actor;

image.png

3、Using index

表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高
如果同时出现Using where,表明索引被用来执行索引键值的查找
如果没用同时出现Using where,表明索引用来读取数据而非执行查找动作。

explain select film_id from film_actor where film_id = 1;

image.png

关于索引会专门写一篇文章介绍。

参考:

https://blog.csdn.net/belalds...
https://blog.csdn.net/UncleMo...

最后,如果喜欢本篇文章,欢迎点赞和收藏。
更多精彩内容请关注我的微信公众号:Python数据科学

查看原文

赞 18 收藏 15 评论 0

Python数据科学 收藏了文章 · 4月11日

厉害了!每30秒学会一个Python小技巧,Github星数4600+

640?wx_fmt=png

作者:xiaoyu,数据爱好者
Python数据科学出品

很多学习Python的朋友在项目实战中会遇到不少功能实现上的问题,有些问题并不是很难的问题,或者已经有了很好的方法来解决。当然,孰能生巧,当我们代码熟练了,自然就能总结一些好用的技巧,不过对于那些还在刚熟悉Python的同学可能并不会那么轻松。

本次给大家推荐一个学习这些技巧的很好的资源“30-seconds-of-python”,所有技巧方法只要30秒就能get到,完全可以利用业务时间不断积累。下面赶紧来看一下。

https://github.com/30-seconds...

内容目录

下面是30秒学Python的整个目录,分为几大板块:ListMathObjectStringUtility,以下是整理的思维脑图。

640?wx_fmt=jpeg

我挑选了10个实用并很有意思的方法分享给大家,其余的感兴趣可以自行学习。

1. List:all_equal
功能实现:检验一个列表中的所有元素是否都一样。
解读:使用[1:] 和 [:-1] 来比较给定列表的所有元素。

def all_equal(lst):
return lst[1:] == lst[:-1]

举例:

all_equal([1, 2, 3, 4, 5, 6]) # False
all_equal([1, 1, 1, 1]) # True

2. List:all_unique
功能实现:如果列表所有值都是唯一的,返回 True,否则 False
解读:在给定列表上使用集合set()去重,比较它和原列表的长度。

def all_unique(lst):
return len(lst) == len(set(lst))

举例:

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False

3. List:bifurcate
功能实现:将列表值分组。如果在filter的元素是True,那么对应的元素属于第一个组;否则属于第二个组。
解读:使用列表推导式和enumerate()基于filter元素到各组。

def bifurcate(lst, filter):
    return [
    [x for i,x in enumerate(lst) if filter[i] == True],
    [x for i,x in enumerate(lst) if filter[i] == False]
  ]

举例:

bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])

# [ ['beep', 'boop', 'bar'], ['foo'] ]

4. List:difference
功能实现:返回两个iterables间的差异。
解读:创建b的集合,使用a的列表推导式保留不在_b中的元素。

def difference(a, b):
  _b = set(b)
return [item for item in a if item not in _b]

举例:

difference([1, 2, 3], [1, 2, 4]) # [3]

5. List:flatten
功能实现:一次性的整合列表。
解读:使用嵌套的列表提取子列表的每个值。

def flatten(lst):
return [x for y in lst for x in y]

举例:

flatten([[1,2,3,4],[5,6,7,8]]) # [1, 2, 3, 4, 5, 6, 7, 8]

6. Math:digitize
功能实现:将一个数分解转换为个位数字。
解读:将n字符化后使用map()函数结合int完成转化

def digitize(n):
return list(map(int, str(n)))

举例:

digitize(123) # [1, 2, 3]

7. List:shuffle
功能实现:将列表元素顺序随机打乱。
解读:使用Fisher-Yates算法重新排序列表元素。

from copy import deepcopy
from random import randint

def shuffle(lst):
  temp_lst = deepcopy(lst)
  m = len(temp_lst)
while (m):
    m -= 1
    i = randint(0, m)
    temp_lst[m], temp_lst[i] = temp_lst[i], temp_lst[m]
return temp_lst

举例:

foo = [1,2,3]
shuffle(foo) # [2,3,1] , foo = [1,2,3]

8. Math:clamp_number
功能实现:将数字num钳在由a和b边界值规定的范围中。
解读:如果num落尽范围内,返回num;否则,返回范围内最接近的数字。

def clamp_number(num,a,b):
return max(min(num, max(a,b)),min(a,b))

举例:

clamp_number(2, 3, 5) # 3
clamp_number(1, -1, -5) # -1

9. String:byte_size
功能实现:返回字符串的字节数。
解读:使用string.encode('utf-8')解码给定字符串,返回长度。

def byte_size(string):
    return len(string.encode('utf-8'))

举例:

byte_size('?') # 4
byte_size('Hello World') # 11

10. Math:gcd
功能实现:计算几个数的最大公因数。
解读:使用reduce()math.gcd在给定列表上实现。

from functools import reduce
import math

def gcd(numbers):
    return reduce(math.gcd, numbers)

举例:

gcd([8,36,28]) # 4

以上就是30秒学python的各种小技巧。怎么样,对于一些常见操作是不是有了一些新的启发,除此之外,还有很多其它技巧可以慢慢学习,希望对各位读者有所帮助。

https://github.com/30-seconds...

最后,如果喜欢本篇文章,欢迎点赞收藏。更多精彩内容请关注Python数据科学

查看原文

Python数据科学 发布了文章 · 4月11日

厉害了!每30秒学会一个Python小技巧,Github星数4600+

640?wx_fmt=png

作者:xiaoyu,数据爱好者
Python数据科学出品

很多学习Python的朋友在项目实战中会遇到不少功能实现上的问题,有些问题并不是很难的问题,或者已经有了很好的方法来解决。当然,孰能生巧,当我们代码熟练了,自然就能总结一些好用的技巧,不过对于那些还在刚熟悉Python的同学可能并不会那么轻松。

本次给大家推荐一个学习这些技巧的很好的资源“30-seconds-of-python”,所有技巧方法只要30秒就能get到,完全可以利用业务时间不断积累。下面赶紧来看一下。

https://github.com/30-seconds...

内容目录

下面是30秒学Python的整个目录,分为几大板块:ListMathObjectStringUtility,以下是整理的思维脑图。

640?wx_fmt=jpeg

我挑选了10个实用并很有意思的方法分享给大家,其余的感兴趣可以自行学习。

1. List:all_equal
功能实现:检验一个列表中的所有元素是否都一样。
解读:使用[1:] 和 [:-1] 来比较给定列表的所有元素。

def all_equal(lst):
return lst[1:] == lst[:-1]

举例:

all_equal([1, 2, 3, 4, 5, 6]) # False
all_equal([1, 1, 1, 1]) # True

2. List:all_unique
功能实现:如果列表所有值都是唯一的,返回 True,否则 False
解读:在给定列表上使用集合set()去重,比较它和原列表的长度。

def all_unique(lst):
return len(lst) == len(set(lst))

举例:

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False

3. List:bifurcate
功能实现:将列表值分组。如果在filter的元素是True,那么对应的元素属于第一个组;否则属于第二个组。
解读:使用列表推导式和enumerate()基于filter元素到各组。

def bifurcate(lst, filter):
    return [
    [x for i,x in enumerate(lst) if filter[i] == True],
    [x for i,x in enumerate(lst) if filter[i] == False]
  ]

举例:

bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])

# [ ['beep', 'boop', 'bar'], ['foo'] ]

4. List:difference
功能实现:返回两个iterables间的差异。
解读:创建b的集合,使用a的列表推导式保留不在_b中的元素。

def difference(a, b):
  _b = set(b)
return [item for item in a if item not in _b]

举例:

difference([1, 2, 3], [1, 2, 4]) # [3]

5. List:flatten
功能实现:一次性的整合列表。
解读:使用嵌套的列表提取子列表的每个值。

def flatten(lst):
return [x for y in lst for x in y]

举例:

flatten([[1,2,3,4],[5,6,7,8]]) # [1, 2, 3, 4, 5, 6, 7, 8]

6. Math:digitize
功能实现:将一个数分解转换为个位数字。
解读:将n字符化后使用map()函数结合int完成转化

def digitize(n):
return list(map(int, str(n)))

举例:

digitize(123) # [1, 2, 3]

7. List:shuffle
功能实现:将列表元素顺序随机打乱。
解读:使用Fisher-Yates算法重新排序列表元素。

from copy import deepcopy
from random import randint

def shuffle(lst):
  temp_lst = deepcopy(lst)
  m = len(temp_lst)
while (m):
    m -= 1
    i = randint(0, m)
    temp_lst[m], temp_lst[i] = temp_lst[i], temp_lst[m]
return temp_lst

举例:

foo = [1,2,3]
shuffle(foo) # [2,3,1] , foo = [1,2,3]

8. Math:clamp_number
功能实现:将数字num钳在由a和b边界值规定的范围中。
解读:如果num落尽范围内,返回num;否则,返回范围内最接近的数字。

def clamp_number(num,a,b):
return max(min(num, max(a,b)),min(a,b))

举例:

clamp_number(2, 3, 5) # 3
clamp_number(1, -1, -5) # -1

9. String:byte_size
功能实现:返回字符串的字节数。
解读:使用string.encode('utf-8')解码给定字符串,返回长度。

def byte_size(string):
    return len(string.encode('utf-8'))

举例:

byte_size('?') # 4
byte_size('Hello World') # 11

10. Math:gcd
功能实现:计算几个数的最大公因数。
解读:使用reduce()math.gcd在给定列表上实现。

from functools import reduce
import math

def gcd(numbers):
    return reduce(math.gcd, numbers)

举例:

gcd([8,36,28]) # 4

以上就是30秒学python的各种小技巧。怎么样,对于一些常见操作是不是有了一些新的启发,除此之外,还有很多其它技巧可以慢慢学习,希望对各位读者有所帮助。

https://github.com/30-seconds...

最后,如果喜欢本篇文章,欢迎点赞收藏。更多精彩内容请关注Python数据科学

查看原文

赞 33 收藏 19 评论 3

Python数据科学 收藏了文章 · 4月5日

那些功能逆天,却鲜为人知的pandas骚操作

作者:东哥

pandas有些功能很逆天,但却鲜为人知,本篇给大家盘点一下。

一、ACCESSOR

pandas有一种功能非常强大的方法,它就是accessor,可以将它理解为一种属性接口,通过它可以获得额外的方法。其实这样说还是很笼统,下面我们通过代码和实例来理解一下。

>>> pd.Series._accessors  
{'cat', 'str', 'dt'}

对于Series数据结构使用_accessors方法,可以得到了3个对象:cat,str,dt。

  • .cat:用于分类数据(Categorical data)
  • .str:用于字符数据(String Object data)
  • .dt:用于时间数据(datetime-like data)

下面我们依次看一下这三个对象是如何使用的。

str对象的使用

Series数据类型:str字符串

 定义一个Series序列

>>> addr = pd.Series([  
...     'Washington, D.C. 20003',  
...     'Brooklyn, NY 11211-1755',  
...     'Omaha, NE 68154',  
...     'Pittsburgh, PA 15211'  
... ])   
  
>>> addr.str.upper()  
0     WASHINGTON, D.C. 20003  
1    BROOKLYN, NY 11211-1755  
2            OMAHA, NE 68154  
3       PITTSBURGH, PA 15211  
dtype: object  
  
>>> addr.str.count(r'\d')   
0    5  
1    9  
2    5  
3    5  
dtype: int64

关于以上str对象的2个方法说明:

  • Series.str.upper:将Series中所有字符串变为大写
  • Series.str.count:对Series中所有字符串的个数进行计数

其实不难发现,该用法的使用与Python中字符串的操作很相似。没错,在pandas中你一样可以这样简单的操作,而不同的是你操作的是一整列的字符串数据。仍然基于以上数据集,再看它的另一个操作:

>>> regex = (r'(?P<city>[A-Za-z ]+), '      # 一个或更多字母  
...          r'(?P<state>[A-Z]{2}) '        # 两个大写字母  
...          r'(?P<zip>\d{5}(?:-\d{4})?)')  # 可选的4个延伸数字  
...  
>>> addr.str.replace('.', '').str.extract(regex)  
         city state         zip  
0  Washington    DC       20003  
1    Brooklyn    NY  11211-1755  
2       Omaha    NE       68154  
3  Pittsburgh    PA       15211

关于以上str对象的2个方法说明:

  • Series.str.replace:将Series中指定字符串替换
  • Series.str.extract:通过正则表达式提取字符串中的数据信息

这个用法就有点复杂了,因为很明显看到,这是一个链式的用法。通过replace将 " . " 替换为"",即为空,紧接着又使用了3个正则表达式(分别对应city,state,zip)通过extract对数据进行了提取,并由原来的Series数据结构变为了DataFrame数据结构。

当然,除了以上用法外,常用的属性和方法还有.rstrip.containssplit等,我们通过下面代码查看一下str属性的完整列表:

>>> [i for i in dir(pd.Series.str) if not i.startswith('_')]  
['capitalize',  
 'cat',  
 'center',  
 'contains',  
 'count',  
 'decode',  
 'encode',  
 'endswith',  
 'extract',  
 'extractall',  
 'find',  
 'findall',  
 'get',  
 'get_dummies',  
 'index',  
 'isalnum',  
 'isalpha',  
 'isdecimal',  
 'isdigit',  
 'islower',  
 'isnumeric',  
 'isspace',  
 'istitle',  
 'isupper',  
 'join',  
 'len',  
 'ljust',  
 'lower',  
 'lstrip',  
 'match',  
 'normalize',  
 'pad',  
 'partition',  
 'repeat',  
 'replace',  
 'rfind',  
 'rindex',  
 'rjust',  
 'rpartition',  
 'rsplit',  
 'rstrip',  
 'slice',  
 'slice_replace',  
 'split',  
 'startswith',  
 'strip',  
 'swapcase',  
 'title',  
 'translate',  
 'upper',  
 'wrap',  
 'zfill']

属性有很多,对于具体的用法,如果感兴趣可以自己进行摸索练习。

dt对象的使用

Series数据类型:datetime

因为数据需要datetime类型,所以下面使用pandas的date_range()生成了一组日期datetime演示如何进行dt对象操作。

>>> daterng = pd.Series(pd.date_range('2017', periods=9, freq='Q'))  
>>> daterng  
0   2017-03-31  
1   2017-06-30  
2   2017-09-30  
3   2017-12-31  
4   2018-03-31  
5   2018-06-30  
6   2018-09-30  
7   2018-12-31  
8   2019-03-31  
dtype: datetime64[ns]  
  
>>>  daterng.dt.day_name()  
0      Friday  
1      Friday  
2    Saturday  
3      Sunday  
4    Saturday  
5    Saturday  
6      Sunday  
7      Monday  
8      Sunday  
dtype: object  
  
>>> # 查看下半年  
>>> daterng\[daterng.dt.quarter > 2]  
2   2017-09-30  
3   2017-12-31  
6   2018-09-30  
7   2018-12-31  
dtype: datetime64[ns]  
  
>>> daterng[daterng.dt.is_year_end]  
3   2017-12-31  
7   2018-12-31  
dtype: datetime64[ns]

以上关于dt的3种方法说明:

  • Series.dt.day_name():从日期判断出所处星期数
  • Series.dt.quarter:从日期判断所处季节
  • Series.dt.is_year_end:从日期判断是否处在年底

其它方法也都是基于datetime的一些变换,并通过变换来查看具体微观或者宏观日期。

cat对象的使用

Series数据类型:Category

在说cat对象的使用前,先说一下Category这个数据类型,它的作用很强大。虽然我们没有经常性的在内存中运行上g的数据,但是我们也总会遇到执行几行代码会等待很久的情况。使用Category数据的一个好处就是:可以很好的节省在时间和空间的消耗。下面我们通过几个实例来学习一下。

>>> colors = pd.Series([  
...     'periwinkle',  
...     'mint green',  
...     'burnt orange',  
...     'periwinkle',  
...     'burnt orange',  
...     'rose',  
...     'rose',  
...     'mint green',  
...     'rose',  
...     'navy'  
... ])  
...  
>>> import sys  
>>> colors.apply(sys.getsizeof)  
0    59  
1    59  
2    61  
3    59  
4    61  
5    53  
6    53  
7    59  
8    53  
9    53  
dtype: int64

上面我们通过使用sys.getsizeof来显示内存占用的情况,数字代表字节数。还有另一种计算内容占用的方法:memory\_usage(),后面会使用。

现在我们将上面colors的不重复值映射为一组整数,然后再看一下占用的内存。

>>> mapper = {v: k for k, v in enumerate(colors.unique())}  
>>> mapper  
{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}  
  
>>> as_int = colors.map(mapper)  
>>> as_int  
0    0  
1    1  
2    2  
3    0  
4    2  
5    3  
6    3  
7    1  
8    3  
9    4  
dtype: int64  
  
>>> as_int.apply(sys.getsizeof)  
0    24  
1    28  
2    28  
3    24  
4    28  
5    28  
6    28  
7    28  
8    28  
9    28  
dtype: int64
注:对于以上的整数值映射也可以使用更简单的pd.factorize()方法代替。

我们发现上面所占用的内存是使用object类型时的一半。其实,这种情况就类似于Category data类型内部的原理。

内存占用区别:Categorical所占用的内存与Categorical分类的数量和数据的长度成正比,相反,object所占用的内存则是一个常数乘以数据的长度。

下面是object内存使用和category内存使用的情况对比。

>>> colors.memory_usage(index=False, deep=True)  
650  
>>> colors.astype('category').memory_usage(index=False, deep=True)  
495

上面结果是使用objectCategory两种情况下内存的占用情况。我们发现效果并没有我们想象中的那么好。但是注意Category内存是成比例的,如果数据集的数据量很大,但不重复分类(unique)值很少的情况下,那么Category的内存占用可以节省达到10倍以上,比如下面数据量增大的情况:

>>> manycolors = colors.repeat(10)  
>>> len(manycolors)/manycolors.nunique()   
20.0  
  
>>> manycolors.memory_usage(index=False, deep=True)  
6500  
>>> manycolors.astype('category').memory_usage(index=False, deep=True)  
585

可以看到,在数据量增加10倍以后,使用Category所占内容节省了10倍以上。

除了占用内存节省外,另一个额外的好处是计算效率有了很大的提升。因为对于Category类型的Series,str字符的操作发生在.cat.categories的非重复值上,而并非原Series上的所有元素上。也就是说对于每个非重复值都只做一次操作,然后再向与非重复值同类的值映射过去。

对于Category的数据类型,可以使用accessor的cat对象,以及相应的属性和方法来操作Category数据。

>>> ccolors = colors.astype('category')  
>>> ccolors.cat.categories  
Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')

实际上,对于开始的整数类型映射,可以先通过reorder_categories进行重新排序,然后再使用cat.codes来实现对整数的映射,来达到同样的效果。

>>> ccolors.cat.reorder_categories(mapper).cat.codes  
0    0  
1    1  
2    2  
3    0  
4    2  
5    3  
6    3  
7    1  
8    3  
9    4  
dtype: int8

dtype类型是Numpy的int8(-127~128)。可以看出以上只需要一个单字节就可以在内存中包含所有的值。我们开始的做法默认使用了int64类型,然而通过pandas的使用可以很智能的将Category数据类型变为最小的类型。

让我们来看一下cat还有什么其它的属性和方法可以使用。下面cat的这些属性基本都是关于查看和操作Category数据类型的。

>>> [i for i in dir(ccolors.cat) if not i.startswith('_')]  
['add_categories',  
 'as_ordered',  
 'as_unordered',  
 'categories',  
 'codes',  
 'ordered',  
 'remove_categories',  
 'remove_unused\_categories',  
 'rename_categories',  
 'reorder_categories',  
 'set_categories']

但是Category数据的使用不是很灵活。例如,插入一个之前没有的值,首先需要将这个值添加到.categories的容器中,然后再添加值。

>>> ccolors.iloc[5] = 'a new color'  
# ...  
ValueError: Cannot setitem on a Categorical with a new category,  
set the categories first  
  
>>> ccolors = ccolors.cat.add\_categories(['a new color'])  
>>> ccolors.iloc[5] = 'a new color'  

如果你想设置值或重塑数据,而非进行新的运算操作,那么Category类型不是那么有用。

二、从clipboard剪切板载入数据

当我们的数据存在excel表里,或者其它的IDE编辑器中的时候,我们想要通过pandas载入数据。我们通常的做法是先保存再载入,其实这样做起来十分繁琐。一个简单的方法就是使用pd.read\_clipboard() 直接从电脑的剪切板缓存区中提取数据。

这样我们就可以直接将结构数据转变为DataFrame或者Series了。excel表中数据是这样的:

在纯文本文件中,比如txt文件,是这样的:

a   b           c       d  
0   1           inf     1/1/00  
2   7.389056099 N/A     5-Jan-13  
4   54.59815003 nan     7/24/18  
6   403.4287935 None    NaT

将上面excel或者txt中的数据选中然后复制,然后使用pandas的read_clipboard()即可完成到DataFrame的转换。parse_dates参数设置为"d",可以自动识别日期,并调整为xxxx-xx-xx的格式。

>>> df = pd.read_clipboard(na_values=[None], parse_dates=['d'])  
>>> df  
   a         b    c          d  
0  0    1.0000  inf 2000-01-01  
1  2    7.3891  NaN 2013-01-05  
2  4   54.5982  NaN 2018-07-24  
3  6  403.4288  NaN        NaT  
  
>>> df.dtypes  
a             int64  
b           float64  
c           float64  
d    datetime64[ns]  
dtype: object

三、将pandas对象转换为“压缩”格式

在pandas中,我们可以直接将objects打包成为gzip, bz2, zip, or xz等压缩格式,而不必将没压缩的文件放在内存中然后进行转化。来看一个例子如何使用:

>>> abalone = pd.read_csv(url, usecols=[0, 1, 2, 3, 4, 8], names=cols)  
  
>>> abalone  
     sex  length   diam  height  weight  rings  
0      M   0.455  0.365   0.095  0.5140     15  
1      M   0.350  0.265   0.090  0.2255      7  
2      F   0.530  0.420   0.135  0.6770      9  
3      M   0.440  0.365   0.125  0.5160     10  
4      I   0.330  0.255   0.080  0.2050      7  
5      I   0.425  0.300   0.095  0.3515      8  
6      F   0.530  0.415   0.150  0.7775     20  
...   ..     ...    ...     ...     ...    ...  
4170   M   0.550  0.430   0.130  0.8395     10  
4171   M   0.560  0.430   0.155  0.8675      8  
4172   F   0.565  0.450   0.165  0.8870     11  
4173   M   0.590  0.440   0.135  0.9660     10  
4174   M   0.600  0.475   0.205  1.1760      9  
4175   F   0.625  0.485   0.150  1.0945     10  
4176   M   0.710  0.555   0.195  1.9485     12

导入文件,读取并存为abalone(DataFrame结构)。当我们要存为压缩的时候,简单的使用 to_json()即可轻松完成转化过程。下面通过设置相应参数将abalone存为了.gz格式的压缩文件。

abalone.to_json('df.json.gz', orient='records',  
                lines=True, compression='gzip')

如果我们想知道储存压缩文件的大小,可以通过内置模块os.path,使用getsize方法来查看文件的字节数。下面是两种格式储存文件的大小对比。

>>> import os.path  
>>> abalone.to_json('df.json', orient='records', lines=True)  
>>> os.path.getsize('df.json') / os.path.getsize('df.json.gz')  
11.603035760226396

四、使用"测试模块"制作伪数据

在pandas中,有一个测试模块可以帮助我们生成半真实(伪数据),并进行测试,它就是util.testing。下面同我们通过一个简单的例子看一下如何生成数据测试:

>>> import pandas.util.testing as tm  
>>> tm.N, tm.K = 15, 3  # 默认的行和列  
  
>>> import numpy as np  
>>> np.random.seed(444)  
  
>>> tm.makeTimeDataFrame(freq='M').head()  
                 A       B       C  
2000-01-31  0.3574 -0.8804  0.2669  
2000-02-29  0.3775  0.1526 -0.4803  
2000-03-31  1.3823  0.2503  0.3008  
2000-04-30  1.1755  0.0785 -0.1791  
2000-05-31 -0.9393 -0.9039  1.1837  
  
>>> tm.makeDataFrame().head()  
                 A       B       C  
nTLGGTiRHF -0.6228  0.6459  0.1251  
WPBRn9jtsR -0.3187 -0.8091  1.1501  
7B3wWfvuDA -1.9872 -1.0795  0.2987  
yJ0BTjehH1  0.8802  0.7403 -1.2154  
0luaYUYvy1 -0.9320  1.2912 -0.2907

上面简单的使用了

makeTimeDataFrame 和 makeDataFrame 分别生成了一组时间数据和DataFrame的数据。但这只是其中的两个用法,关于testing中的方法有大概30多个,如果你想全部了解,可以通过查看dir获得:

>>> [i for i in dir(tm) if i.startswith('make')]  
['makeBoolIndex',  
 'makeCategoricalIndex',  
 'makeCustomDataframe',  
 'makeCustomIndex',  
 # ...,  
 'makeTimeSeries',  
 'makeTimedeltaIndex',  
 'makeUIntIndex',  
 'makeUnicodeIndex']

五、从列项中创建DatetimeIndex

也许我们有的时候会遇到这样的情形(为了说明这种情情况,我使用了product进行交叉迭代的创建了一组关于时间的数据):

>>> from itertools import product  
>>> datecols = ['year', 'month', 'day']  
  
>>> df = pd.DataFrame(list(product([2017, 2016], [1, 2], [1, 2, 3])),  
...                   columns=datecols)  
>>> df['data'] = np.random.randn(len(df))  
>>> df  
    year  month  day    data  
0   2017      1    1 -0.0767  
1   2017      1    2 -1.2798  
2   2017      1    3  0.4032  
3   2017      2    1  1.2377  
4   2017      2    2 -0.2060  
5   2017      2    3  0.6187  
6   2016      1    1  2.3786  
7   2016      1    2 -0.4730  
8   2016      1    3 -2.1505  
9   2016      2    1 -0.6340  
10  2016      2    2  0.7964  
11  2016      2    3  0.0005

明显看到,列项中有year,month,day,它们分别在各个列中,而并非是一个完整日期。那么如何从这些列中将它们组合在一起并设置为新的index呢?

通过to_datetime的使用,我们就可以直接将年月日组合为一个完整的日期,然后赋给索引。代码如下:

>>> df.index = pd.to_datetime(df[datecols])  
>>> df.head()  
            year  month  day    data  
2017-01-01  2017      1    1 -0.0767  
2017-01-02  2017      1    2 -1.2798  
2017-01-03  2017      1    3  0.4032  
2017-02-01  2017      2    1  1.2377  
2017-02-02  2017      2    2 -0.2060

当然,你可以选择将原有的年月日列移除,只保留data数据列,然后squeeze转换为Series结构。

>>> df = df.drop(datecols, axis=1).squeeze()  
>>> df.head()  
2017-01-01   -0.0767  
2017-01-02   -1.2798  
2017-01-03    0.4032  
2017-02-01    1.2377  
2017-02-02   -0.2060  
Name: data, dtype: float64  
  
>>> df.index.dtype_str  
'datetime64[ns]

更多精彩内容请关注Python数据科学

查看原文

Python数据科学 发布了文章 · 4月5日

那些功能逆天,却鲜为人知的pandas骚操作

作者:东哥

pandas有些功能很逆天,但却鲜为人知,本篇给大家盘点一下。

一、ACCESSOR

pandas有一种功能非常强大的方法,它就是accessor,可以将它理解为一种属性接口,通过它可以获得额外的方法。其实这样说还是很笼统,下面我们通过代码和实例来理解一下。

>>> pd.Series._accessors  
{'cat', 'str', 'dt'}

对于Series数据结构使用_accessors方法,可以得到了3个对象:cat,str,dt。

  • .cat:用于分类数据(Categorical data)
  • .str:用于字符数据(String Object data)
  • .dt:用于时间数据(datetime-like data)

下面我们依次看一下这三个对象是如何使用的。

str对象的使用

Series数据类型:str字符串

 定义一个Series序列

>>> addr = pd.Series([  
...     'Washington, D.C. 20003',  
...     'Brooklyn, NY 11211-1755',  
...     'Omaha, NE 68154',  
...     'Pittsburgh, PA 15211'  
... ])   
  
>>> addr.str.upper()  
0     WASHINGTON, D.C. 20003  
1    BROOKLYN, NY 11211-1755  
2            OMAHA, NE 68154  
3       PITTSBURGH, PA 15211  
dtype: object  
  
>>> addr.str.count(r'\d')   
0    5  
1    9  
2    5  
3    5  
dtype: int64

关于以上str对象的2个方法说明:

  • Series.str.upper:将Series中所有字符串变为大写
  • Series.str.count:对Series中所有字符串的个数进行计数

其实不难发现,该用法的使用与Python中字符串的操作很相似。没错,在pandas中你一样可以这样简单的操作,而不同的是你操作的是一整列的字符串数据。仍然基于以上数据集,再看它的另一个操作:

>>> regex = (r'(?P<city>[A-Za-z ]+), '      # 一个或更多字母  
...          r'(?P<state>[A-Z]{2}) '        # 两个大写字母  
...          r'(?P<zip>\d{5}(?:-\d{4})?)')  # 可选的4个延伸数字  
...  
>>> addr.str.replace('.', '').str.extract(regex)  
         city state         zip  
0  Washington    DC       20003  
1    Brooklyn    NY  11211-1755  
2       Omaha    NE       68154  
3  Pittsburgh    PA       15211

关于以上str对象的2个方法说明:

  • Series.str.replace:将Series中指定字符串替换
  • Series.str.extract:通过正则表达式提取字符串中的数据信息

这个用法就有点复杂了,因为很明显看到,这是一个链式的用法。通过replace将 " . " 替换为"",即为空,紧接着又使用了3个正则表达式(分别对应city,state,zip)通过extract对数据进行了提取,并由原来的Series数据结构变为了DataFrame数据结构。

当然,除了以上用法外,常用的属性和方法还有.rstrip.containssplit等,我们通过下面代码查看一下str属性的完整列表:

>>> [i for i in dir(pd.Series.str) if not i.startswith('_')]  
['capitalize',  
 'cat',  
 'center',  
 'contains',  
 'count',  
 'decode',  
 'encode',  
 'endswith',  
 'extract',  
 'extractall',  
 'find',  
 'findall',  
 'get',  
 'get_dummies',  
 'index',  
 'isalnum',  
 'isalpha',  
 'isdecimal',  
 'isdigit',  
 'islower',  
 'isnumeric',  
 'isspace',  
 'istitle',  
 'isupper',  
 'join',  
 'len',  
 'ljust',  
 'lower',  
 'lstrip',  
 'match',  
 'normalize',  
 'pad',  
 'partition',  
 'repeat',  
 'replace',  
 'rfind',  
 'rindex',  
 'rjust',  
 'rpartition',  
 'rsplit',  
 'rstrip',  
 'slice',  
 'slice_replace',  
 'split',  
 'startswith',  
 'strip',  
 'swapcase',  
 'title',  
 'translate',  
 'upper',  
 'wrap',  
 'zfill']

属性有很多,对于具体的用法,如果感兴趣可以自己进行摸索练习。

dt对象的使用

Series数据类型:datetime

因为数据需要datetime类型,所以下面使用pandas的date_range()生成了一组日期datetime演示如何进行dt对象操作。

>>> daterng = pd.Series(pd.date_range('2017', periods=9, freq='Q'))  
>>> daterng  
0   2017-03-31  
1   2017-06-30  
2   2017-09-30  
3   2017-12-31  
4   2018-03-31  
5   2018-06-30  
6   2018-09-30  
7   2018-12-31  
8   2019-03-31  
dtype: datetime64[ns]  
  
>>>  daterng.dt.day_name()  
0      Friday  
1      Friday  
2    Saturday  
3      Sunday  
4    Saturday  
5    Saturday  
6      Sunday  
7      Monday  
8      Sunday  
dtype: object  
  
>>> # 查看下半年  
>>> daterng\[daterng.dt.quarter > 2]  
2   2017-09-30  
3   2017-12-31  
6   2018-09-30  
7   2018-12-31  
dtype: datetime64[ns]  
  
>>> daterng[daterng.dt.is_year_end]  
3   2017-12-31  
7   2018-12-31  
dtype: datetime64[ns]

以上关于dt的3种方法说明:

  • Series.dt.day_name():从日期判断出所处星期数
  • Series.dt.quarter:从日期判断所处季节
  • Series.dt.is_year_end:从日期判断是否处在年底

其它方法也都是基于datetime的一些变换,并通过变换来查看具体微观或者宏观日期。

cat对象的使用

Series数据类型:Category

在说cat对象的使用前,先说一下Category这个数据类型,它的作用很强大。虽然我们没有经常性的在内存中运行上g的数据,但是我们也总会遇到执行几行代码会等待很久的情况。使用Category数据的一个好处就是:可以很好的节省在时间和空间的消耗。下面我们通过几个实例来学习一下。

>>> colors = pd.Series([  
...     'periwinkle',  
...     'mint green',  
...     'burnt orange',  
...     'periwinkle',  
...     'burnt orange',  
...     'rose',  
...     'rose',  
...     'mint green',  
...     'rose',  
...     'navy'  
... ])  
...  
>>> import sys  
>>> colors.apply(sys.getsizeof)  
0    59  
1    59  
2    61  
3    59  
4    61  
5    53  
6    53  
7    59  
8    53  
9    53  
dtype: int64

上面我们通过使用sys.getsizeof来显示内存占用的情况,数字代表字节数。还有另一种计算内容占用的方法:memory\_usage(),后面会使用。

现在我们将上面colors的不重复值映射为一组整数,然后再看一下占用的内存。

>>> mapper = {v: k for k, v in enumerate(colors.unique())}  
>>> mapper  
{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}  
  
>>> as_int = colors.map(mapper)  
>>> as_int  
0    0  
1    1  
2    2  
3    0  
4    2  
5    3  
6    3  
7    1  
8    3  
9    4  
dtype: int64  
  
>>> as_int.apply(sys.getsizeof)  
0    24  
1    28  
2    28  
3    24  
4    28  
5    28  
6    28  
7    28  
8    28  
9    28  
dtype: int64
注:对于以上的整数值映射也可以使用更简单的pd.factorize()方法代替。

我们发现上面所占用的内存是使用object类型时的一半。其实,这种情况就类似于Category data类型内部的原理。

内存占用区别:Categorical所占用的内存与Categorical分类的数量和数据的长度成正比,相反,object所占用的内存则是一个常数乘以数据的长度。

下面是object内存使用和category内存使用的情况对比。

>>> colors.memory_usage(index=False, deep=True)  
650  
>>> colors.astype('category').memory_usage(index=False, deep=True)  
495

上面结果是使用objectCategory两种情况下内存的占用情况。我们发现效果并没有我们想象中的那么好。但是注意Category内存是成比例的,如果数据集的数据量很大,但不重复分类(unique)值很少的情况下,那么Category的内存占用可以节省达到10倍以上,比如下面数据量增大的情况:

>>> manycolors = colors.repeat(10)  
>>> len(manycolors)/manycolors.nunique()   
20.0  
  
>>> manycolors.memory_usage(index=False, deep=True)  
6500  
>>> manycolors.astype('category').memory_usage(index=False, deep=True)  
585

可以看到,在数据量增加10倍以后,使用Category所占内容节省了10倍以上。

除了占用内存节省外,另一个额外的好处是计算效率有了很大的提升。因为对于Category类型的Series,str字符的操作发生在.cat.categories的非重复值上,而并非原Series上的所有元素上。也就是说对于每个非重复值都只做一次操作,然后再向与非重复值同类的值映射过去。

对于Category的数据类型,可以使用accessor的cat对象,以及相应的属性和方法来操作Category数据。

>>> ccolors = colors.astype('category')  
>>> ccolors.cat.categories  
Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')

实际上,对于开始的整数类型映射,可以先通过reorder_categories进行重新排序,然后再使用cat.codes来实现对整数的映射,来达到同样的效果。

>>> ccolors.cat.reorder_categories(mapper).cat.codes  
0    0  
1    1  
2    2  
3    0  
4    2  
5    3  
6    3  
7    1  
8    3  
9    4  
dtype: int8

dtype类型是Numpy的int8(-127~128)。可以看出以上只需要一个单字节就可以在内存中包含所有的值。我们开始的做法默认使用了int64类型,然而通过pandas的使用可以很智能的将Category数据类型变为最小的类型。

让我们来看一下cat还有什么其它的属性和方法可以使用。下面cat的这些属性基本都是关于查看和操作Category数据类型的。

>>> [i for i in dir(ccolors.cat) if not i.startswith('_')]  
['add_categories',  
 'as_ordered',  
 'as_unordered',  
 'categories',  
 'codes',  
 'ordered',  
 'remove_categories',  
 'remove_unused\_categories',  
 'rename_categories',  
 'reorder_categories',  
 'set_categories']

但是Category数据的使用不是很灵活。例如,插入一个之前没有的值,首先需要将这个值添加到.categories的容器中,然后再添加值。

>>> ccolors.iloc[5] = 'a new color'  
# ...  
ValueError: Cannot setitem on a Categorical with a new category,  
set the categories first  
  
>>> ccolors = ccolors.cat.add\_categories(['a new color'])  
>>> ccolors.iloc[5] = 'a new color'  

如果你想设置值或重塑数据,而非进行新的运算操作,那么Category类型不是那么有用。

二、从clipboard剪切板载入数据

当我们的数据存在excel表里,或者其它的IDE编辑器中的时候,我们想要通过pandas载入数据。我们通常的做法是先保存再载入,其实这样做起来十分繁琐。一个简单的方法就是使用pd.read\_clipboard() 直接从电脑的剪切板缓存区中提取数据。

这样我们就可以直接将结构数据转变为DataFrame或者Series了。excel表中数据是这样的:

在纯文本文件中,比如txt文件,是这样的:

a   b           c       d  
0   1           inf     1/1/00  
2   7.389056099 N/A     5-Jan-13  
4   54.59815003 nan     7/24/18  
6   403.4287935 None    NaT

将上面excel或者txt中的数据选中然后复制,然后使用pandas的read_clipboard()即可完成到DataFrame的转换。parse_dates参数设置为"d",可以自动识别日期,并调整为xxxx-xx-xx的格式。

>>> df = pd.read_clipboard(na_values=[None], parse_dates=['d'])  
>>> df  
   a         b    c          d  
0  0    1.0000  inf 2000-01-01  
1  2    7.3891  NaN 2013-01-05  
2  4   54.5982  NaN 2018-07-24  
3  6  403.4288  NaN        NaT  
  
>>> df.dtypes  
a             int64  
b           float64  
c           float64  
d    datetime64[ns]  
dtype: object

三、将pandas对象转换为“压缩”格式

在pandas中,我们可以直接将objects打包成为gzip, bz2, zip, or xz等压缩格式,而不必将没压缩的文件放在内存中然后进行转化。来看一个例子如何使用:

>>> abalone = pd.read_csv(url, usecols=[0, 1, 2, 3, 4, 8], names=cols)  
  
>>> abalone  
     sex  length   diam  height  weight  rings  
0      M   0.455  0.365   0.095  0.5140     15  
1      M   0.350  0.265   0.090  0.2255      7  
2      F   0.530  0.420   0.135  0.6770      9  
3      M   0.440  0.365   0.125  0.5160     10  
4      I   0.330  0.255   0.080  0.2050      7  
5      I   0.425  0.300   0.095  0.3515      8  
6      F   0.530  0.415   0.150  0.7775     20  
...   ..     ...    ...     ...     ...    ...  
4170   M   0.550  0.430   0.130  0.8395     10  
4171   M   0.560  0.430   0.155  0.8675      8  
4172   F   0.565  0.450   0.165  0.8870     11  
4173   M   0.590  0.440   0.135  0.9660     10  
4174   M   0.600  0.475   0.205  1.1760      9  
4175   F   0.625  0.485   0.150  1.0945     10  
4176   M   0.710  0.555   0.195  1.9485     12

导入文件,读取并存为abalone(DataFrame结构)。当我们要存为压缩的时候,简单的使用 to_json()即可轻松完成转化过程。下面通过设置相应参数将abalone存为了.gz格式的压缩文件。

abalone.to_json('df.json.gz', orient='records',  
                lines=True, compression='gzip')

如果我们想知道储存压缩文件的大小,可以通过内置模块os.path,使用getsize方法来查看文件的字节数。下面是两种格式储存文件的大小对比。

>>> import os.path  
>>> abalone.to_json('df.json', orient='records', lines=True)  
>>> os.path.getsize('df.json') / os.path.getsize('df.json.gz')  
11.603035760226396

四、使用"测试模块"制作伪数据

在pandas中,有一个测试模块可以帮助我们生成半真实(伪数据),并进行测试,它就是util.testing。下面同我们通过一个简单的例子看一下如何生成数据测试:

>>> import pandas.util.testing as tm  
>>> tm.N, tm.K = 15, 3  # 默认的行和列  
  
>>> import numpy as np  
>>> np.random.seed(444)  
  
>>> tm.makeTimeDataFrame(freq='M').head()  
                 A       B       C  
2000-01-31  0.3574 -0.8804  0.2669  
2000-02-29  0.3775  0.1526 -0.4803  
2000-03-31  1.3823  0.2503  0.3008  
2000-04-30  1.1755  0.0785 -0.1791  
2000-05-31 -0.9393 -0.9039  1.1837  
  
>>> tm.makeDataFrame().head()  
                 A       B       C  
nTLGGTiRHF -0.6228  0.6459  0.1251  
WPBRn9jtsR -0.3187 -0.8091  1.1501  
7B3wWfvuDA -1.9872 -1.0795  0.2987  
yJ0BTjehH1  0.8802  0.7403 -1.2154  
0luaYUYvy1 -0.9320  1.2912 -0.2907

上面简单的使用了

makeTimeDataFrame 和 makeDataFrame 分别生成了一组时间数据和DataFrame的数据。但这只是其中的两个用法,关于testing中的方法有大概30多个,如果你想全部了解,可以通过查看dir获得:

>>> [i for i in dir(tm) if i.startswith('make')]  
['makeBoolIndex',  
 'makeCategoricalIndex',  
 'makeCustomDataframe',  
 'makeCustomIndex',  
 # ...,  
 'makeTimeSeries',  
 'makeTimedeltaIndex',  
 'makeUIntIndex',  
 'makeUnicodeIndex']

五、从列项中创建DatetimeIndex

也许我们有的时候会遇到这样的情形(为了说明这种情情况,我使用了product进行交叉迭代的创建了一组关于时间的数据):

>>> from itertools import product  
>>> datecols = ['year', 'month', 'day']  
  
>>> df = pd.DataFrame(list(product([2017, 2016], [1, 2], [1, 2, 3])),  
...                   columns=datecols)  
>>> df['data'] = np.random.randn(len(df))  
>>> df  
    year  month  day    data  
0   2017      1    1 -0.0767  
1   2017      1    2 -1.2798  
2   2017      1    3  0.4032  
3   2017      2    1  1.2377  
4   2017      2    2 -0.2060  
5   2017      2    3  0.6187  
6   2016      1    1  2.3786  
7   2016      1    2 -0.4730  
8   2016      1    3 -2.1505  
9   2016      2    1 -0.6340  
10  2016      2    2  0.7964  
11  2016      2    3  0.0005

明显看到,列项中有year,month,day,它们分别在各个列中,而并非是一个完整日期。那么如何从这些列中将它们组合在一起并设置为新的index呢?

通过to_datetime的使用,我们就可以直接将年月日组合为一个完整的日期,然后赋给索引。代码如下:

>>> df.index = pd.to_datetime(df[datecols])  
>>> df.head()  
            year  month  day    data  
2017-01-01  2017      1    1 -0.0767  
2017-01-02  2017      1    2 -1.2798  
2017-01-03  2017      1    3  0.4032  
2017-02-01  2017      2    1  1.2377  
2017-02-02  2017      2    2 -0.2060

当然,你可以选择将原有的年月日列移除,只保留data数据列,然后squeeze转换为Series结构。

>>> df = df.drop(datecols, axis=1).squeeze()  
>>> df.head()  
2017-01-01   -0.0767  
2017-01-02   -1.2798  
2017-01-03    0.4032  
2017-02-01    1.2377  
2017-02-02   -0.2060  
Name: data, dtype: float64  
  
>>> df.index.dtype_str  
'datetime64[ns]

更多精彩内容请关注Python数据科学

查看原文

赞 18 收藏 12 评论 0

Python数据科学 关注了专栏 · 4月5日

猪哥python

跟着猪哥学Python吧~

关注 2553

Python数据科学 发布了文章 · 2019-09-03

数据探索很麻烦?推荐一款史上最强大的特征分析可视化工具:yellowbrick

作者:xiaoyu

微信公众号:Python数据科学


clipboard.png

前言

玩过建模的朋友都知道,在建立模型之前有很长的一段特征工程工作要做,而在特征工程的过程中,探索性数据分析又是必不可少的一部分,因为如果我们要对各个特征进行细致的分析,那么必然会进行一些可视化以辅助我们来做选择和判断。

可视化的工具有很多,但是能够针对特征探索性分析而进行专门可视化的不多,今天给大家介绍一款功能十分强大的工具:yellowbrick,希望通过这个工具的辅助可以节省更多探索的时间,快速掌握特征信息。

功能

雷达 RadViz

RadViz雷达图是一种多变量数据可视化算法,它围绕圆周均匀地分布每个特征,并且标准化了每个特征值。一般数据科学家使用此方法来检测类之间的关联。例如,是否有机会从特征集中学习一些东西或是否有太多的噪音?

# Load the classification data set
data = load_data("occupancy")

# Specify the features of interest and the classes of the target
features = ["temperature", "relative humidity", "light", "C02", "humidity"]
classes = ["unoccupied", "occupied"]

# Extract the instances and target
X = data[features]
y = data.occupancy

# Import the visualizer
from yellowbrick.features import RadViz

# Instantiate the visualizer
visualizer = RadViz(classes=classes, features=features)

visualizer.fit(X, y)      # Fit the data to the visualizer
visualizer.transform(X)   # Transform the data
visualizer.poof()         # Draw/show/poof the data

clipboard.png

从上面雷达图可以看出5个维度中,温度对于目标类的影响是比较大的。

一维排序 Rank 1D

特征的一维排序利用排名算法,仅考虑单个特征,默认情况下使用Shapiro-Wilk算法来评估与特征相关的实例分布的正态性,然后绘制一个条形图,显示每个特征的相对等级。

from yellowbrick.features import Rank1D

# Instantiate the 1D visualizer with the Sharpiro ranking algorithm
visualizer = Rank1D(features=features, algorithm='shapiro')

visualizer.fit(X, y)                # Fit the data to the visualizer
visualizer.transform(X)             # Transform the data
visualizer.poof()                   # Draw/show/poof the data

clipboard.png

PCA Projection

PCA分解可视化利用主成分分析将高维数据分解为二维或三维,以便可以在散点图中绘制每个实例。PCA的使用意味着可以沿主要变化轴分析投影数据集,并且可以解释该数据集以确定是否可以利用球面距离度量。

clipboard.png

双重图 Biplot

PCA投影可以增强到双点,其点是投影实例,其矢量表示高维空间中数据的结构。通过使用proj_features = True标志,数据集中每个要素的向量将在散点图上以该要素的最大方差方向绘制。这些结构可用于分析特征对分解的重要性或查找相关方差的特征以供进一步分析。

# Load the classification data set
data = load_data('concrete')

# Specify the features of interest and the target
target = "strength"
features = [
    'cement', 'slag', 'ash', 'water', 'splast', 'coarse', 'fine', 'age'
]

# Extract the instance data and the target
X = data[features]
y = data[target]

visualizer = PCADecomposition(scale=True, proj_features=True)
visualizer.fit_transform(X, y)
visualizer.poof()

clipboard.png

特征重要性 Feature Importance

特征工程过程涉及选择生成有效模型所需的最小特征,因为模型包含的特征越多,它就越复杂(数据越稀疏),因此模型对方差的误差越敏感。消除特征的常用方法是描述它们对模型的相对重要性,然后消除弱特征或特征组合并重新评估以确定模型在交叉验证期间是否更好。

在scikit-learn中,Decision Tree模型和树的集合(如Random Forest,Gradient Boosting和AdaBoost)在拟合时提供feature_importances_属性。Yellowbrick FeatureImportances可视化工具利用此属性对相对重要性进行排名和绘制。

import matplotlib.pyplot as plt

from sklearn.ensemble import GradientBoostingClassifier

from yellowbrick.features.importances import FeatureImportances

# Create a new matplotlib figure
fig = plt.figure()
ax = fig.add_subplot()

viz = FeatureImportances(GradientBoostingClassifier(), ax=ax)
viz.fit(X, y)
viz.poof()

clipboard.png

递归特征消除 Recursive Feature Elimination

递归特征消除(RFE)是一种特征选择方法,它训练模型并删除最弱的特征(或多个特征),直到达到指定数量的特征。特征按模型的coef_或feature_importances_属性排序,并通过递归消除每个循环的少量特征,RFE尝试消除模型中可能存在的依赖性和共线性。

RFE需要保留指定数量的特征,但事先通常不知道有多少特征有效。为了找到最佳数量的特征,交叉验证与RFE一起用于对不同的特征子集进行评分,并选择最佳评分特征集合。RFECV可视化绘制模型中的特征数量以及它们的交叉验证测试分数和可变性,并可视化所选数量的特征。

from sklearn.svm import SVC
from sklearn.datasets import make_classification

from yellowbrick.features import RFECV

# Create a dataset with only 3 informative features
X, y = make_classification(
    n_samples=1000, n_features=25, n_informative=3, n_redundant=2,
    n_repeated=0, n_classes=8, n_clusters_per_class=1, random_state=0
)

# Create RFECV visualizer with linear SVM classifier
viz = RFECV(SVC(kernel='linear', C=1))
viz.fit(X, y)
viz.poof()

clipboard.png

该图显示了理想的RFECV曲线,当捕获三个信息特征时,曲线跳跃到极好的准确度,然后随着非信息特征被添加到模型中,精度逐渐降低。阴影区域表示交叉验证的可变性,一个标准偏差高于和低于曲线绘制的平均精度得分。

下面是一个真实数据集,我们可以看到RFECV对信用违约二元分类器的影响。

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold

df = load_data('credit')

target = 'default'
features = [col for col in data.columns if col != target]

X = data[features]
y = data[target]

cv = StratifiedKFold(5)
oz = RFECV(RandomForestClassifier(), cv=cv, scoring='f1_weighted')

oz.fit(X, y)
oz.poof()

clipboard.png

在这个例子中,我们可以看到选择了19个特征,尽管在大约5个特征之后模型的f1分数似乎没有太大改善。选择要消除的特征在确定每个递归的结果中起着重要作用;修改步骤参数以在每个步骤中消除多个特征可能有助于尽早消除最差特征,增强其余特征(并且还可用于加速具有大量特征的数据集的特征消除)。

残差图 Residuals Plot

在回归模型的上下文中,残差是目标变量(y)的观测值与预测值(ŷ)之间的差异,例如,预测的错误。残差图显示垂直轴上的残差与水平轴上的因变量之间的差异,允许检测目标中可能容易出错或多或少的误差的区域。

from sklearn.linear_model import Ridge
from yellowbrick.regressor import ResidualsPlot

# Instantiate the linear model and visualizer
ridge = Ridge()
visualizer = ResidualsPlot(ridge)

visualizer.fit(X_train, y_train)  # Fit the training data to the model
visualizer.score(X_test, y_test)  # Evaluate the model on the test data
visualizer.poof()                 # Draw/show/poof the data

clipboard.png

正则化 Alpha Selection

正则化旨在惩罚模型复杂性,因此α越高,模型越复杂,由于方差(过度拟合)而减少误差。另一方面,太高的Alpha会因偏差(欠调)而增加误差。因此,重要的是选择最佳α,以便在两个方向上最小化误差。 AlphaSelection Visualizer演示了不同的α值如何影响线性模型正则化过程中的模型选择。一般而言,α增加了正则化的影响,例如,如果alpha为零,则没有正则化,α越高,正则化参数对最终模型的影响越大。

import numpy as np

from sklearn.linear_model import LassoCV
from yellowbrick.regressor import AlphaSelection

# Create a list of alphas to cross-validate against
alphas = np.logspace(-10, 1, 400)

# Instantiate the linear model and visualizer
model = LassoCV(alphas=alphas)
visualizer = AlphaSelection(model)

visualizer.fit(X, y)
g = visualizer.poof()

clipboard.png

分类预测误差 Class Prediction Error

类预测误差图提供了一种快速了解分类器在预测正确类别方面有多好的方法。

from sklearn.ensemble import RandomForestClassifier

from yellowbrick.classifier import ClassPredictionError

# Instantiate the classification model and visualizer
visualizer = ClassPredictionError(
    RandomForestClassifier(), classes=classes
)

# Fit the training data to the visualizer
visualizer.fit(X_train, y_train)

# Evaluate the model on the test data
visualizer.score(X_test, y_test)

# Draw visualization
g = visualizer.poof()

clipboard.png

当然也同时有分类评估指标的可视化,包括混淆矩阵、AUC/ROC、召回率/精准率等等。

二分类辨别阈值 Discrimination Threshold

关于二元分类器的辨别阈值的精度,召回,f1分数和queue rate的可视化。辨别阈值是在阴性类别上选择正类别的概率或分数。通常,将其设置为50%,但可以调整阈值以增加或降低对误报或其他应用因素的敏感度。

from sklearn.linear_model import LogisticRegression
from yellowbrick.classifier import DiscriminationThreshold

# Instantiate the classification model and visualizer
logistic = LogisticRegression()
visualizer = DiscriminationThreshold(logistic)

visualizer.fit(X, y)  # Fit the training data to the visualizer
visualizer.poof()     # Draw/show/poof the data

clipboard.png

聚类肘部法则 Elbow Method

KElbowVisualizer实现了“肘部”法则,通过使模型具有K的一系列值来帮助数据科学家选择最佳簇数。如果折线图类似于手臂,那么“肘”(拐点)就是曲线)是一个很好的迹象,表明基础模型最适合那一点。

在下面的示例中,KElbowVisualizer在具有8个随机点集的样本二维数据集上适合KMeans模型,以获得4到11的K值范围。当模型适合8个聚类时,我们可以在图中看到“肘部”,在这种情况下,我们知道它是最佳数字。

from sklearn.datasets import make_blobs

# Create synthetic dataset with 8 random clusters
X, y = make_blobs(centers=8, n_features=12, shuffle=True, random_state=42)

from sklearn.cluster import KMeans
from yellowbrick.cluster import KElbowVisualizer

# Instantiate the clustering model and visualizer
model = KMeans()
visualizer = KElbowVisualizer(model, k=(4,12))

visualizer.fit(X)    # Fit the data to the visualizer
visualizer.poof()    # Draw/show/poof the data

clipboard.png

集群间距离图 Intercluster Distance Maps

集群间距离地图以2维方式显示集群中心的嵌入,并保留与其他中心的距离。例如。中心越靠近可视化,它们就越接近原始特征空间。根据评分指标调整集群的大小。默认情况下,它们按内部数据的多少,例如属于每个中心的实例数。这给出了集群的相对重要性。但请注意,由于两个聚类在2D空间中重叠,因此并不意味着它们在原始特征空间中重叠。

from sklearn.datasets import make_blobs

# Make 12 blobs dataset
X, y = make_blobs(centers=12, n_samples=1000, n_features=16, shuffle=True)

from sklearn.cluster import KMeans
from yellowbrick.cluster import InterclusterDistance

# Instantiate the clustering model and visualizer
visualizer = InterclusterDistance(KMeans(9))

visualizer.fit(X) # Fit the training data to the visualizer
visualizer.poof() # Draw/show/poof the data

clipboard.png

模型选择-学习曲线 Learning Curve

学习曲线基于不同数量的训练样本,检验模型训练分数与交叉验证测试分数的关系。这种可视化通常用来表达两件事:

  1. 模型会不会随着数据量增多而效果变好
  2. 模型对偏差和方差哪个更加敏感

下面是利用yellowbrick生成的学习曲线可视化图。该学习曲线对于分类、回归和聚类都可以适用。

clipboard.png

模型选择-验证曲线 Validation Curve

模型验证用于确定模型对其已经过训练的数据的有效性以及它对新输入的泛化程度。为了测量模型的性能,我们首先将数据集拆分为训练和测试,将模型拟合到训练数据上并在保留的测试数据上进行评分。

为了最大化分数,必须选择模型的超参数,以便最好地允许模型在指定的特征空间中操作。大多数模型都有多个超参数,选择这些参数组合的最佳方法是使用网格搜索。然而,绘制单个超参数对训练和测试数据的影响有时是有用的,以确定模型是否对某些超参数值不适合或过度拟合。

import numpy as np

from sklearn.tree import DecisionTreeRegressor
from yellowbrick.model_selection import ValidationCurve

# Load a regression dataset
data = load_data('energy')

# Specify features of interest and the target
targets = ["heating load", "cooling load"]
features = [col for col in data.columns if col not in targets]

# Extract the instances and target
X = data[features]
y = data[targets[0]]

viz = ValidationCurve(
    DecisionTreeRegressor(), param_name="max_depth",
    param_range=np.arange(1, 11), cv=10, scoring="r2"
)

# Fit and poof the visualizer
viz.fit(X, y)
viz.poof()

clipboard.png

总结

个人认为yellowbrick这个工具非常好,一是因为解决了特征工程和建模过程中的可视化问题,极大地简化了操作;二是通过各种可视化也可以补充自己对建模的一些盲区。

本篇仅展示了建模中部分可视化功能,详细的完整功能请参考:

https://www.scikit-yb.org/en/...

如果觉得有帮助,还请给点个赞!

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

查看原文

赞 28 收藏 23 评论 0

Python数据科学 发布了文章 · 2019-07-21

Python一行代码搞定炫酷可视化,你需要了解一下Cufflinks

作者:xiaoyu

微信公众号:Python数据科学

知乎:python数据分析师


前言

学过Python数据分析的朋友都知道,在可视化的工具中,有很多优秀的三方库,比如matplotlibseabornplotlyBokenpyecharts等等。这些可视化库都有自己的特点,在实际应用中也广为大家使用。

plotly、Boken等都是交互式的可视化工具,结合Jupyter notebook可以非常灵活方便地展现分析后的结果。虽然做出的效果非常的炫酷,比如plotly,但是每一次都需要写很长的代码,一是麻烦,二是不便于维护。

我觉得在数据的分析阶段,更多的时间应该放在分析上,维度选择、拆解合并,业务理解和判断。如果既可以减少代码量,又可以做出炫酷可视化效果,那将大大提高效率。当然如果有特别的需求除外,此方法仅针对想要快速可视化进行分析的人。

本篇给大家介绍一个非常棒的工具,cufflinks,可以完美解决这个问题,且效果一样炫酷。

cufflinks介绍

就像seaborn封装了matplotlib一样,cufflinks在plotly的基础上做了一进一步的包装,方法统一,参数配置简单。其次它还可以结合pandas的dataframe随意灵活地画图。可以把它形容为"pandas like visualization"。

毫不夸张地说,画出各种炫酷的可视化图形,我只需一行代码,效率非常高,同时也降低了使用的门槛儿。cufflinks的github链接如下:

https://github.com/santosjorg...

cufflinks安装

安装不多说,直接pip install即可。

pip install cufflinks

cufflinks如何使用?

cufflinks库一直在不断更新,目前最新版为V0.14.0,支持plotly3.0。首先我们看看它都支持哪些种类的图形,可以通过help来查看。


import cufflinks as cf
cf.help()

Use 'cufflinks.help(figure)' to see the list of available parameters for the given figure.
Use 'DataFrame.iplot(kind=figure)' to plot the respective figure
Figures:
  bar
  box
  bubble
  bubble3d
  candle
  choroplet
  distplot
  heatmap
  histogram
  ohlc
  pie
  ratio
  scatter
  scatter3d
  scattergeo
  spread
  surface
  violin

使用方法其实很简单,我总结一下,它的格式大致是这样的:

  • DataFrame:代表pandas的数据框;
  • Figure:代表我们上面看到的可绘制图形,比如bar、box、histogram等等;
  • iplot:代表绘制方法,其中有很多参数可以进行配置,调节符合你自己风格的可视化图形;

cufflinks实例

我们通过几个实例感受一下上面的使用方法。使用过plotly的朋友可能知道,如果使用online模式,那么生成的图形是有限制的。所以,我们这里先设置为offline模式,这样就避免了出现次数限制问题。

import pandas as pd
import cufflinks as cf
import numpy as np

cf.set_config_file(offline=True)

然后我们需要按照上面的使用格式来操作,首先我们需要有个DataFrame,如果手头没啥数据,那可以先生成个随机数。cufflinks有一个专门生成随机数的方法,叫做datagen,用于生成不同维度的随机数据,比如下面。

lines线图

cf.datagen.lines(1,500).ta_plot(study='sma',periods=[13,21,55])

1)cufflinks使用datagen生成随机数;

2)figure定义为lines形式,数据为(1,500);

3)然后再用ta_plot绘制这一组时间序列,参数设置SMA展现三个不同周期的时序分析。

box箱型图

还是与上面用法一样,一行代码解决。

cf.datagen.box(20).iplot(kind='box',legend=False)

可以看到,x轴每个box都有对应的名称,这是因为cufflinks通过kind参数识别了box图形,自动为它生成的名字。如果我们只生成随机数,它是这样子的,默认生成100行的随机分布的数据,列数由自己选定。

histogram直方图

cf.datagen.histogram(3).iplot(kind='histogram')

和plotly一样,我们可以通过一些辅助的小工具框选或者lasso选择来区分和选定指定区域,只要一行代码。

当然了,除了随机数据,任何的其它dataframe数据框都可以,包括我们自己导入的数据。

histogram条形图

df=pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
df.iplot(kind='bar',barmode='stack')

上面我们生成了一个(10,4)的dataframe数据框,名称分别是a,b,c,d。那么cufflinks将会根据iplot中的kind种类自动识别并绘制图形。参数设置为堆叠模式。

scatter散点图

df = pd.DataFrame(np.random.rand(50, 4), columns=['a', 'b', 'c', 'd'])
df.iplot(kind='scatter',mode='markers',colors=['orange','teal','blue','yellow'],size=10)

bubble气泡图

df.iplot(kind='bubble',x='a',y='b',size='c')

scatter matrix 散点矩阵图

df = pd.DataFrame(np.random.randn(1000, 4), columns=['a', 'b', 'c', 'd'])
df.scatter_matrix()

subplots 子图

df=cf.datagen.lines(4)
df.iplot(subplots=True,shape=(4,1),shared_xaxes=True,vertical_spacing=.02,fill=True)

df.iplot(subplots=True,subplot_titles=True,legend=False)

再比如复杂一点的。

df=cf.datagen.bubble(10,50,mode='stocks')
figs=cf.figures(df,[dict(kind='histogram',keys='x',color='blue'),
                    dict(kind='scatter',mode='markers',x='x',y='y',size=5),
                    dict(kind='scatter',mode='markers',x='x',y='y',size=5,color='teal')],asList=True)
figs.append(cf.datagen.lines(1).figure(bestfit=True,colors=['blue'],bestfit_colors=['pink']))
base_layout=cf.tools.get_base_layout(figs)
sp=cf.subplots(figs,shape=(3,2),base_layout=base_layout,vertical_spacing=.15,horizontal_spacing=.03,
               specs=[[{'rowspan':2},{}],[None,{}],[{'colspan':2},None]],
               subplot_titles=['Histogram','Scatter 1','Scatter 2','Bestfit Line'])
sp['layout'].update(showlegend=False)
cf.iplot(sp)

shapes 形状图

如果我们想在lines图上增加一些直线作为参考基准,这时候我们可以使用hlines的类型图。

df=cf.datagen.lines(3,columns=['a','b','c'])
df.iplot(hline=[dict(y=-1,color='blue',width=3),dict(y=1,color='pink',dash='dash')])

或者是将某个区域标记出来,可以使用hspan类型。

df.iplot(hspan=[(-1,1),(2,5)])

又或者是竖条的区域,可以用vspan类型。

df.iplot(vspan={'x0':'2015-02-15','x1':'2015-03-15','color':'teal','fill':True,'opacity':.4})

如果对iplot中的参数不熟练,直接输入以下代码即可查询。

help(df.iplot)

总结

怎么样,是不是非常快捷方便?以上介绍是一般的可绘制类型,当然你可以根据自己的需求做出更多的可视化图形。如果是常规图形,一行即可实现。除此外,cufflinks还有强大的颜色管理功能,如果感兴趣可以自行学习。

如果觉得有帮助,还请给点个赞!

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

查看原文

赞 8 收藏 5 评论 0

Python数据科学 发布了文章 · 2019-02-12

还在抱怨pandas运行速度慢?这几个方法会颠覆你的看法

作者:xiaoyu

微信公众号:Python数据科学

知乎:python数据分析师


图片描述

前言

当大家谈到数据分析时,提及最多的语言就是PythonSQL。Python之所以适合数据分析,是因为它有很多第三方强大的库来协助,pandas就是其中之一。pandas的文档中是这样描述的:

“快速,灵活,富有表现力的数据结构,旨在使”关系“或”标记“数据的使用既简单又直观。”

我们知道pandas的两个主要数据结构:dataframeseries,我们对数据的一些操作都是基于这两个数据结构的。但在实际的使用中,我们可能很多时候会感觉运行一些数据结构的操作会异常的慢。一个操作慢几秒可能看不出来什么,但是一整个项目中很多个操作加起来会让整个开发工作效率变得很低。有的朋友抱怨pandas简直太慢了,其实对于pandas的一些操作也是有一定技巧的。

pandas是基于numpy库的数组结构上构建的,并且它的很多操作都是(通过numpy或者pandas自身由Cpython实现并编译成C的扩展模块)在C语言中实现的。因此,如果正确使用pandas的话,它的运行速度应该是非常快的。

本篇将要介绍几种pandas中常用到的方法,对于这些方法使用存在哪些需要注意的问题,以及如何对它们进行速度提升。

  • 将datetime数据与时间序列一起使用的优点
  • 进行批量计算的最有效途径
  • 通过HDFStore存储数据节省时间

使用Datetime数据节省时间

我们来看一个例子。

>>> import pandas as pd
>>> pd.__version__
'0.23.1'

# 导入数据集
>>> df = pd.read_csv('demand_profile.csv')
>>> df.head()
     date_time  energy_kwh
0  1/1/13 0:00       0.586
1  1/1/13 1:00       0.580
2  1/1/13 2:00       0.572
3  1/1/13 3:00       0.596
4  1/1/13 4:00       0.592

从运行上面代码得到的结果来看,好像没有什么问题。但实际上pandas和numpy都有一个dtypes 的概念。如果没有特殊声明,那么date_time将会使用一个 object 的dtype类型,如下面代码所示:

>>> df.dtypes
date_time      object
energy_kwh    float64
dtype: object

>>> type(df.iat[0, 0])
str

object 类型像一个大的容器,不仅仅可以承载 str,也可以包含那些不能很好地融进一个数据类型的任何特征列。而如果我们将日期作为 str 类型就会极大的影响效率。

因此,对于时间序列的数据而言,我们需要让上面的date_time列格式化为datetime对象数组(pandas称之为时间戳)。pandas在这里操作非常简单,操作如下:

>>> df['date_time'] = pd.to_datetime(df['date_time'])
>>> df['date_time'].dtype
datetime64[ns]

我们来运行一下这个df看看转化后的效果是什么样的。

>>> df.head()
               date_time    energy_kwh
0    2013-01-01 00:00:00         0.586
1    2013-01-01 01:00:00         0.580
2    2013-01-01 02:00:00         0.572
3    2013-01-01 03:00:00         0.596
4    2013-01-01 04:00:00         0.592

date_time的格式已经自动转化了,但这还没完,在这个基础上,我们还是可以继续提高运行速度的。如何提速呢?为了更好的对比,我们首先通过 timeit 装饰器来测试一下上面代码的转化时间。

>>> @timeit(repeat=3, number=10)
... def convert(df, column_name):
...     return pd.to_datetime(df[column_name])

>>> df['date_time'] = convert(df, 'date_time')
Best of 3 trials with 10 function calls per trial:
Function `convert` ran in average of 1.610 seconds.

1.61s,看上去挺快,但其实可以更快,我们来看一下下面的方法。

>>> @timeit(repeat=3, number=100)
>>> def convert_with_format(df, column_name):
...     return pd.to_datetime(df[column_name],
...                           format='%d/%m/%y %H:%M')
Best of 3 trials with 100 function calls per trial:
Function `convert_with_format` ran in average of 0.032 seconds.

结果只有0.032s,快了将近50倍。原因是:我们设置了转化的格式format。由于在CSV中的datetimes并不是 ISO 8601 格式的,如果不进行设置的话,那么pandas将使用 dateutil 包把每个字符串str转化成date日期。

相反,如果原始数据datetime已经是 ISO 8601 格式了,那么pandas就可以立即使用最快速的方法来解析日期。这也就是为什么提前设置好格式format可以提升这么多。

pandas数据的循环操作

仍然基于上面的数据,我们想添加一个新的特征,但这个新的特征是基于一些时间条件的,根据时长(小时)而变化,如下:

clipboard.png

因此,按照我们正常的做法就是使用apply方法写一个函数,函数里面写好时间条件的逻辑代码。

def apply_tariff(kwh, hour):
 """计算每个小时的电费"""  
    if 0 <= hour < 7:
        rate = 12
    elif 7 <= hour < 17:
        rate = 20
    elif 17 <= hour < 24:
        rate = 28
    else:
        raise ValueError(f'Invalid hour: {hour}')
    return rate * kwh

然后使用for循环来遍历df,根据apply函数逻辑添加新的特征,如下:

>>> # 不赞同这种操作
>>> @timeit(repeat=3, number=100)
... def apply_tariff_loop(df):
...     """Calculate costs in loop.  Modifies `df` inplace."""
...     energy_cost_list = []
...     for i in range(len(df)):
...         # 获取用电量和时间(小时)
...         energy_used = df.iloc[i]['energy_kwh']
...         hour = df.iloc[i]['date_time'].hour
...         energy_cost = apply_tariff(energy_used, hour)
...         energy_cost_list.append(energy_cost)
...     df['cost_cents'] = energy_cost_list
... 
>>> apply_tariff_loop(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_loop` ran in average of 3.152 seconds.

对于那些写Pythonic风格的人来说,这个设计看起来很自然。然而,这个循环将会严重影响效率,也是不赞同这么做。原因有几个:

  • 首先,它需要初始化一个将记录输出的列表。
  • 其次,它使用不透明对象范围(0,len(df))循环,然后在应用apply_tariff()之后,它必须将结果附加到用于创建新DataFrame列的列表中。它还使用df.iloc [i] ['date_time']执行所谓的链式索引,这通常会导致意外的结果。
  • 但这种方法的最大问题是计算的时间成本。对于8760行数据,此循环花费了3秒钟。接下来,你将看到一些改进的Pandas结构迭代解决方案。

使用itertuples() 和iterrows() 循环

那么推荐做法是什么样的呢?

实际上可以通过pandas引入itertuples和iterrows方法可以使效率更快。这些都是一次产生一行的生成器方法,类似scrapy中使用的yield用法。

.itertuples为每一行产生一个namedtuple,并且行的索引值作为元组的第一个元素。nametuple是Python的collections模块中的一种数据结构,其行为类似于Python元组,但具有可通过属性查找访问的字段。

.iterrows为DataFrame中的每一行产生(index,series)这样的元组。

虽然.itertuples往往会更快一些,但是在这个例子中使用.iterrows,我们看看这使用iterrows后效果如何。

>>> @timeit(repeat=3, number=100)
... def apply_tariff_iterrows(df):
...     energy_cost_list = []
...     for index, row in df.iterrows():
...         # 获取用电量和时间(小时)
...         energy_used = row['energy_kwh']
...         hour = row['date_time'].hour
...         # 添加cost列表
...         energy_cost = apply_tariff(energy_used, hour)
...         energy_cost_list.append(energy_cost)
...     df['cost_cents'] = energy_cost_list
...
>>> apply_tariff_iterrows(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_iterrows` ran in average of 0.713 seconds.

语法方面:这样的语法更明确,并且行值引用中的混乱更少,因此它更具可读性。

在时间收益方面:快了近5倍! 但是,还有更多的改进空间。我们仍然在使用某种形式的Python for循环,这意味着每个函数调用都是在Python中完成的,理想情况是它可以用Pandas内部架构中内置的更快的语言完成。

Pandas的 .apply()方法

我们可以使用.apply方法而不是.iterrows进一步改进此操作。Pandas的.apply方法接受函数(callables)并沿DataFrame的轴(所有行或所有列)应用它们。在此示例中,lambda函数将帮助你将两列数据传递给apply_tariff()

>>> @timeit(repeat=3, number=100)
... def apply_tariff_withapply(df):
...     df['cost_cents'] = df.apply(
...         lambda row: apply_tariff(
...             kwh=row['energy_kwh'],
...             hour=row['date_time'].hour),
...         axis=1)
...
>>> apply_tariff_withapply(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_withapply` ran in average of 0.272 seconds.

.apply的语法优点很明显,行数少,代码可读性高。在这种情况下,所花费的时间大约是.iterrows方法的一半。

但是,这还不是“非常快”。一个原因是.apply()将在内部尝试循环遍历Cython迭代器。但是在这种情况下,传递的lambda不是可以在Cython中处理的东西,因此它在Python中调用,因此并不是那么快。

如果你使用.apply()获取10年的小时数据,那么你将需要大约15分钟的处理时间。如果这个计算只是大型模型的一小部分,那么你真的应该加快速度。这也就是矢量化操作派上用场的地方。

矢量化操作:使用.isin()选择数据

什么是矢量化操作?如果你不基于一些条件,而是可以在一行代码中将所有电力消耗数据应用于该价格(df ['energy_kwh'] * 28),类似这种。这个特定的操作就是矢量化操作的一个例子,它是在Pandas中执行的最快方法。

但是如何将条件计算应用为Pandas中的矢量化运算?一个技巧是根据你的条件选择和分组DataFrame,然后对每个选定的组应用矢量化操作。 在下一个示例中,你将看到如何使用Pandas的.isin()方法选择行,然后在向量化操作中实现上面新特征的添加。在执行此操作之前,如果将date_time列设置为DataFrame的索引,则会使事情更方便:

df.set_index('date_time', inplace=True)

@timeit(repeat=3, number=100)
def apply_tariff_isin(df):
# 定义小时范围Boolean数组
    peak_hours = df.index.hour.isin(range(17, 24))
    shoulder_hours = df.index.hour.isin(range(7, 17))
    off_peak_hours = df.index.hour.isin(range(0, 7))

    # 使用上面的定义
    df.loc[peak_hours, 'cost_cents'] = df.loc[peak_hours, 'energy_kwh'] * 28
    df.loc[shoulder_hours,'cost_cents'] = df.loc[shoulder_hours, 'energy_kwh'] * 20
    df.loc[off_peak_hours,'cost_cents'] = df.loc[off_peak_hours, 'energy_kwh'] * 12

我们来看一下结果如何。

>>> apply_tariff_isin(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_isin` ran in average of 0.010 seconds.

为了了解刚才代码中发生的情况,我们需要知道.isin()方法返回的是一个布尔值数组,如下所示:

[False, False, False, ..., True, True, True]

这些值标识哪些DataFrame索引(datetimes)落在指定的小时范围内。然后,当你将这些布尔数组传递给DataFrame的.loc索引器时,你将获得一个仅包含与这些小时匹配的行的DataFrame切片。在那之后,仅仅是将切片乘以适当的费率,这是一种快速的矢量化操作。

这与我们上面的循环操作相比如何?首先,你可能会注意到不再需要apply_tariff(),因为所有条件逻辑都应用于行的选择。因此,你必须编写的代码行和调用的Python代码会大大减少。

处理时间怎么样?比不是Pythonic的循环快315倍,比.iterrows快71倍,比.apply快27倍。

还可以做的更好吗?

在apply_tariff_isin中,我们仍然可以通过调用df.locdf.index.hour.isin三次来进行一些“手动工作”。如果我们有更精细的时隙范围,你可能会争辩说这个解决方案是不可扩展的。幸运的是,在这种情况下,你可以使用Pandas的pd.cut() 函数以编程方式执行更多操作:

@timeit(repeat=3, number=100)
def apply_tariff_cut(df):
    cents_per_kwh = pd.cut(x=df.index.hour,
                           bins=[0, 7, 17, 24],
                           include_lowest=True,
                           labels=[12, 20, 28]).astype(int)
    df['cost_cents'] = cents_per_kwh * df['energy_kwh']

让我们看看这里发生了什么。pd.cut() 根据每小时所属的bin应用一组标签(costs)。

注意include_lowest参数表示第一个间隔是否应该是包含左边的(您希望在组中包含时间= 0)。
这是一种完全矢量化的方式来获得我们的预期结果,它在时间方面是最快的:
>>> apply_tariff_cut(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_cut` ran in average of 0.003 seconds. 

到目前为止,时间上基本快达到极限了,只需要花费不到一秒的时间来处理完整的10年的小时数据集。但是,最后一个选项是使用 NumPy 函数来操作每个DataFrame的底层NumPy数组,然后将结果集成回Pandas数据结构中。

使用Numpy继续加速

使用Pandas时不应忘记的一点是Pandas SeriesDataFrames是在NumPy库之上设计的。这为你提供了更多的计算灵活性,因为Pandas可以与NumPy阵列和操作无缝衔接。

下面,我们将使用NumPy的 digitize() 函数。它类似于Pandas的cut(),因为数据将被分箱,但这次它将由一个索引数组表示,这些索引表示每小时所属的bin。然后将这些索引应用于价格数组:

@timeit(repeat=3, number=100)
def apply_tariff_digitize(df):
    prices = np.array([12, 20, 28])
    bins = np.digitize(df.index.hour.values, bins=[7, 17, 24])
    df['cost_cents'] = prices[bins] * df['energy_kwh'].values

与cut函数一样,这种语法非常简洁易读。但它在速度方面有何比较?让我们来看看:

>>> apply_tariff_digitize(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_digitize` ran in average of 0.002 seconds.

在这一点上,仍然有性能提升,但它本质上变得更加边缘化。使用Pandas,它可以帮助维持“层次结构”,如果你愿意,可以像在此处一样进行批量计算,这些通常排名从最快到最慢(最灵活到最不灵活):

1. 使用向量化操作:没有for循环的Pandas方法和函数。

2. 将.apply方法:与可调用方法一起使用。

3. 使用.itertuples:从Python的集合模块迭代DataFrame行作为namedTuples。

4. 使用.iterrows:迭代DataFrame行作为(index,Series)对。虽然Pandas系列是一种灵活的数据结构,但将每一行构建到一个系列中然后访问它可能会很昂贵。

5. 使用“element-by-element”循环:使用df.loc或df.iloc一次更新一个单元格或行。
图片描述

使用HDFStore防止重新处理

现在你已经了解了Pandas中的加速数据流程,接着让我们探讨如何避免与最近集成到Pandas中的HDFStore一起重新处理时间。

通常,在构建复杂数据模型时,可以方便地对数据进行一些预处理。例如,如果您有10年的分钟频率耗电量数据,即使你指定格式参数,只需将日期和时间转换为日期时间可能需要20分钟。你真的只想做一次,而不是每次运行你的模型,进行测试或分析。

你可以在此处执行的一项非常有用的操作是预处理,然后将数据存储在已处理的表单中,以便在需要时使用。但是,如何以正确的格式存储数据而无需再次重新处理?如果你要另存为CSV,则只会丢失datetimes对象,并且在再次访问时必须重新处理它。

Pandas有一个内置的解决方案,它使用 HDF5,这是一种专门用于存储表格数据阵列的高性能存储格式。 Pandas的 HDFStore 类允许你将DataFrame存储在HDF5文件中,以便可以有效地访问它,同时仍保留列类型和其他元数据。它是一个类似字典的类,因此您可以像读取Python dict对象一样进行读写。

以下是将预处理电力消耗DataFrame df存储在HDF5文件中的方法:

# 创建储存对象,并存为 processed_data
data_store = pd.HDFStore('processed_data.h5')

# 将 DataFrame 放进对象中,并设置 key 为 preprocessed_df
data_store['preprocessed_df'] = df
data_store.close()

现在,你可以关闭计算机并休息一下。等你回来的时候,你处理的数据将在你需要时为你所用,而无需再次加工。以下是如何从HDF5文件访问数据,并保留数据类型:

# 获取数据储存对象
data_store = pd.HDFStore('processed_data.h5')

# 通过key获取数据
preprocessed_df = data_store['preprocessed_df']
data_store.close()

数据存储可以容纳多个表,每个表的名称作为键。

关于在Pandas中使用HDFStore的注意事项:您需要安装PyTables> = 3.0.0,因此在安装Pandas之后,请确保更新PyTables,如下所示:

pip install --upgrade tables

结论

如果你觉得你的Pandas项目不够快速,灵活,简单和直观,请考虑重新考虑你使用该库的方式。

这里探讨的示例相当简单,但说明了Pandas功能的正确应用如何能够大大改进运行时和速度的代码可读性。以下是一些经验,可以在下次使用Pandas中的大型数据集时应用这些经验法则:

  • 尝试尽可能使用矢量化操作,而不是在df 中解决for x的问题。如果你的代码是许多for循环,那么它可能更适合使用本机Python数据结构,因为Pandas会带来很多开销。
  • 如果你有更复杂的操作,其中矢量化根本不可能或太难以有效地解决,请使用.apply方法。
  • 如果必须循环遍历数组(确实发生了这种情况),请使用.iterrows()或.itertuples()来提高速度和语法。
  • Pandas有很多可选性,几乎总有几种方法可以从A到B。请注意这一点,比较不同方法的执行方式,并选择在项目环境中效果最佳的路线。
  • 一旦建立了数据清理脚本,就可以通过使用HDFStore存储中间结果来避免重新处理。
  • 将NumPy集成到Pandas操作中通常可以提高速度并简化语法。
参考:https://realpython.com/fast-f...

如果觉得有帮助,还请给点个赞!

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

图片描述

查看原文

赞 20 收藏 15 评论 0

Python数据科学 收藏了文章 · 2018-11-11

Hexo 个人博客部署到 CentOS 个人服务器

Hexo 一个快速,简单和强大的博客框架,基于 Node.js。

<!-- more -->

目标

  • 在一台 CentOS 7.2 的 ECS 云服务器上快速部署基于 Hexo 的博客站点

  • 可以在本地简洁快速发布一篇博文到个人云服务器上, 用于个人站点展示

准备工作

  • 你能操作的个人电脑PC * 1台

  • 自己能控制的服务器Sever * 1台

服务器配置

默认 ROOT 权限登录

  • 安装 Git Nginx
    升级 CentOS 所有包,包括系统版本内核升级

    yum -y update
    yum install -y git nginx
  • Nginx 配置
    创建文件目录, 用于博客站点文件存放, 并更改目录读写权限

  1. -p /data/www/hexo

  2. -R $USER:$USER /data/www/hexo

  3. -R 755 /data/www/hexo

添加 index.html 用于检测配置 Nginx 是否成功

vim /data/www/hexo/index.html

添加如下代码:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="UTF-8">
  </head>
  <body>
    <p>Nginx running</p>
  </body>
</html>

配置 Nginx 服务器

vim /etc/nginx/nginx.conf

# vim 查找: /listen 80

通过 vim 查找功能找到如下代码, 并修改

......
server {
      listen       80 default_server;
      listen       [::]:80 default_server;
      server_name  www.xxx.com; # 填写个人域名
      root         /data/www/hexo;
  }
......

访问服务器 IP 或者域名显示

Nginx running

Nginx 配置成功

  • Git 配置
    创建文件目录, 用于私人 Git 仓库搭建, 并更改目录读写权限

  1. -p /data/GitLibrary

  2. -R $USER:$USER /data/GitLibrary

  3. -R 755 /data/GitLibrary

Git 初始化裸库

cd /data/GitLibrary
git init --bare hexo.git

创建 Git 钩子(hook)

vim /data/GitLibrary/hexo.git/hooks/post-receive

用于指定 Git 的源代码 和 Git 配置文件

#!/bin/bash
git --work-tree=/data/www/hexo --git-dir=/data/GitLibrary/hexo.git checkout -f

保存并退出后, 给该文件添加可执行权限

chmod +x /data/GitLibrary/hexo.git/hooks/post-receive

本地配置

这篇文章在 MacOS 上安装 node.js Git Hexo等

Windows & Linux 可搜索对应平台软件安装

  • 安装 Git
    在 Terminal.app(终端) 中输入:

      xcode-select --install

    按照提示完成安装, 最后在 Terminal 中输入

      git -v

    如下显示, 表示 Git 安装成功

      git version 2.11.0 (Apple Git-81)
  • 安装 Node.js 和 Npm
    Node.js 安装在 MacOS 中需要用到 Homebrew 进行安装管理

    Homebrew 在 MacOS 里类似于 CentOS 的 yum

    在 Terminal 输入:

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

    使用 Homebrew 安装 Node.js

    brew install node

    在 Terminal 中键入类似的命令, 如下显示, 表示 Node 和 Npm 安装成功

    node -v
    v7.10.0
    npm -v
    4.2.0
  • 安装 Hexo 及相关插件
    全局安装 hexo-cli 和 hexo-server

    npm install hexo-cli hexo-server hexo-deployer-git -g

    安装完成后, 在本地初始化博客站点搭建

    hexo init ~/myBlog
    
    # ~/myBlog 即本地存放路径

    完成安装, 便可以在本地路径查看项目

  • 本地 Hexo 配置
    进人~/myBlog 目录, 修改 Hexo 博客站点配置文件 _config.yml, 如下修改:

    title: 页面标题     //页面标题
    subtitle: 小标题     //小标题
    description: 描述     //描述
    author: 作者       //作者
    language: zh-Hans     //语言
    timezone:       //时区
    
    # URL
    ### If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
    url: http://leyliu.com      //个人域名
    
    ......
    
    # Deployment
    ### Docs: https://hexo.io/docs/deployment.html
    deploy:     //发布对应的github账号
    type: git
    repo: root@leyliu.com:/data/GitLibrary/hexo  //用户名@域名或 IP 地址:/data/GitLibrary/hexo
    branch: master

部署步骤

将本地部署到服务器

  • 清除缓存

    hexo clean
  • 生成静态页面:

    hexo generate
  • 将本地静态页面目录部署到云服务器

    hexo deploy

    完成 Hexo 个人博客网站搭建, 通过服务器 IP 或者域名即可访问

结束语

以上就是我个人采用Hexo+云服务器搭建个人博客的全部流程,较为完整地介绍了 Hexo 博客的安装及简单配置,服务端如何配置通过 Git 部署 Nginx 及 Linux 简单应用。
在云服务器上创建私有 Git 仓库, 通过 Git 钩子,将 Hexo 生成的博客静态页面文件,推送到 Nginx 服务的托管目录, 完成部署。

如有疑问, 可留言或者搜索引擎解决

查看原文

Python数据科学 发布了文章 · 2018-10-15

【机器学习笔记】:一文让你彻底记住什么是ROC/AUC(看不懂你来找我)

作者:xiaoyu

微信公众号:Python数据科学

知乎:python数据分析师


ROC/AUC作为机器学习的评估指标非常重要,也是面试中经常出现的问题(80%都会问到)。其实,理解它并不是非常难,但是好多朋友都遇到了一个相同的问题,那就是:每次看书的时候都很明白,但回过头就忘了,经常容易将概念弄混。还有的朋友面试之前背下来了,但是一紧张大脑一片空白全忘了,导致回答的很差。

我在之前的面试过程中也遇到过类似的问题,我的面试经验是:一般笔试题遇到选择题基本都会考这个率,那个率,或者给一个场景让你选用哪个。面试过程中也被问过很多次,比如什么是AUC/ROC?横轴纵轴都代表什么?有什么优点?为什么要使用它?

我记得在我第一次回答的时候,我将准确率,精准率,召回率等概念混淆了,最后一团乱。回去以后我从头到尾梳理了一遍所有相关概念,后面的面试基本都回答地很好。现在想将自己的一些理解分享给大家,希望读完本篇可以彻底记住ROC/AUC的概念。

什么是性能度量?

我们都知道机器学习要建模,但是对于模型性能的好坏(即模型的泛化能力),我们并不知道是怎样的,很可能这个模型就是一个差的模型,泛化能力弱,对测试集不能很好的预测或分类。那么如何知道这个模型是好是坏呢?我们必须有个评判的标准。为了了解模型的泛化能力,我们需要用某个指标来衡量,这就是性能度量的意义。有了一个指标,我们就可以对比不同模型了,从而知道哪个模型相对好,那个模型相对差,并通过这个指标来进一步调参逐步优化我们的模型。

当然,对于分类和回归两类监督学习,分别有各自的评判标准。本篇我们主要讨论与分类相关的一些指标,因为AUC/ROC就是用于分类的性能度量标准。

混淆矩阵,准确率,精准率,召回率

1. 混淆矩阵

在介绍各个率之前,先来介绍一下混淆矩阵。如果我们用的是个二分类的模型,那么把预测情况与实际情况的所有结果两两混合,结果就会出现以下4种情况,就组成了混淆矩阵。

由于1和0是数字,阅读性不好,所以我们分别用P和N表示1和0两种结果。变换之后为PP,PN,NP,NN,阅读性也很差,我并不能轻易地看出来预测的正确性与否。因此,为了能够更清楚地分辨各种预测情况是否正确,我们将其中一个符号修改为T和F,以便于分辨出结果。

P(Positive):代表1

N(Negative):代表0

T(True):代表预测正确

F(False):代表错误

按照上面的字符表示重新分配矩阵,混淆矩阵就变成了下面这样:

clipboard.png

将这种表示方法总结如下,可分为两部分:

clipboard.png

因此对于这种表示方法可以这么简单的理解:先看 ①预测结果(P/N),再根据②实际表现对比预测结果,给出判断结果(T/F)。按这个顺序理解,这四种情况就很好记住了。

TP:预测为1,预测正确,即实际1

FP:预测为1,预测错误,即实际0

FN:预测为0,预测错确,即实际1

TN:预测为0,预测正确即,实际0

2. 准确率

既然是个分类指标,我们可以很自然的想到准确率,准确率的定义是预测正确的结果占总样本的百分比,其公式如下:

准确率=(TP+TN)/(TP+TN+FP+FN)

虽然准确率可以判断总的正确率,但是在样本不平衡的情况下,并不能作为很好的指标来衡量结果。举个简单的例子,比如在一个总样本中,正样本占90%,负样本占10%,样本是严重不平衡的。对于这种情况,我们只需要将全部样本预测为正样本即可得到90%的高准确率,但实际上我们并没有很用心的分类,只是随便无脑一分而已。这就说明了:由于样本不平衡的问题,导致了得到的高准确率结果含有很大的水分。即如果样本不平衡,准确率就会失效。

正因为如此,也就衍生出了其它两种指标:精准率和召回率。

3. 精准率

精准率(Precision)又叫查准率,它是针对预测结果而言的,它的含义是在所有被预测为正的样本中实际为正的样本的概率,意思就是在预测为正样本的结果中,我们有多少把握可以预测正确,其公式如下:

精准率=TP/(TP+FP)

clipboard.png

精准率和准确率看上去有些类似,但是完全不同的两个概念。精准率代表对正样本结果中的预测准确程度,而准确率则代表整体的预测准确程度,既包括正样本,也包括负样本。

4. 召回率

召回率(Recall)又叫查全率,它是针对原样本而言的,它的含义是在实际为正的样本中被预测为正样本的概率,其公式如下:

召回率=TP/(TP+FN)

clipboard.png

召回率的应用场景:比如拿网贷违约率为例,相对好用户,我们更关心坏用户,不能错放过任何一个坏用户。因为如果我们过多的将坏用户当成好用户,这样后续可能发生的违约金额会远超过好用户偿还的借贷利息金额,造成严重偿失。召回率越高,代表实际坏用户被预测出来的概率越高,它的含义类似:宁可错杀一千,绝不放过一个。

5. 精准率和召回率的关系,F1分数

通过上面的公式,我们发现:精准率和召回率的分子是相同,都是TP,但分母是不同的,一个是(TP+FP),一个是(TP+FN)。两者的关系可以用一个P-R图来展示:

clipboard.png

如何理解P-R(查准率-查全率)这条曲线?

有的朋友疑惑:这条曲线是根据什么变化的?为什么是这个形状的曲线?其实这要从排序型模型说起。拿逻辑回归举例,逻辑回归的输出是一个0到1之间的概率数字,因此,如果我们想要根据这个概率判断用户好坏的话,我们就必须定义一个阈值。通常来讲,逻辑回归的概率越大说明越接近1,也就可以说他是坏用户的可能性更大。比如,我们定义了阈值为0.5,即概率小于0.5的我们都认为是好用户,而大于0.5都认为是坏用户。因此,对于阈值为0.5的情况下,我们可以得到相应的一对查准率和查全率。

但问题是:这个阈值是我们随便定义的,我们并不知道这个阈值是否符合我们的要求。因此,为了找到一个最合适的阈值满足我们的要求,我们就必须遍历0到1之间所有的阈值,而每个阈值下都对应着一对查准率和查全率,从而我们就得到了这条曲线。

有的朋友又问了:如何找到最好的阈值点呢?首先,需要说明的是我们对于这两个指标的要求:我们希望查准率和查全率同时都非常高。但实际上这两个指标是一对矛盾体,无法做到双高。图中明显看到,如果其中一个非常高,另一个肯定会非常低。选取合适的阈值点要根据实际需求,比如我们想要高的查全率,那么我们就会牺牲一些查准率,在保证查全率最高的情况下,查准率也不那么低。

F1分数

但通常,如果想要找到二者之间的一个平衡点,我们就需要一个新的指标:F1分数。F1分数同时考虑了查准率和查全率,让二者同时达到最高,取一个平衡。F1分数的公式为 = 2查准率查全率 / (查准率 + 查全率)。我们在图中看到的平衡点就是F1分数得来的结果。

ROC/AUC的概念

1. 灵敏度,特异度,真正率,假正率

在正式介绍ROC/AUC之前,我们还要再介绍两个指标,这两个指标的选择也正是ROC和AUC可以无视样本不平衡的原因。这两个指标分别是:灵敏度和(1-特异度),也叫做真正率(TPR)和假正率(FPR)。

灵敏度(Sensitivity) = TP/(TP+FN)

特异度(Specificity) = TN/(FP+TN)

其实我们可以发现灵敏度和召回率是一模一样的,只是名字换了而已。

由于我们比较关心正样本,所以需要查看有多少负样本被错误地预测为正样本,所以使用(1-特异度),而不是特异度。

真正率(TPR) = 灵敏度 = TP/(TP+FN)

假正率(FPR) = 1- 特异度 = FP/(FP+TN)

下面是真正率和假正率的示意,我们发现TPR和FPR分别是基于实际表现1和0出发的,也就是说它们分别在实际的正样本和负样本中来观察相关概率问题。正因为如此,所以无论样本是否平衡,都不会被影响。还是拿之前的例子,总样本中,90%是正样本,10%是负样本。我们知道用准确率是有水分的,但是用TPR和FPR不一样。这里,TPR只关注90%正样本中有多少是被真正覆盖的,而与那10%毫无关系,同理,FPR只关注10%负样本中有多少是被错误覆盖的,也与那90%毫无关系,所以可以看出:如果我们从实际表现的各个结果角度出发,就可以避免样本不平衡的问题了,这也是为什么选用TPR和FPR作为ROC/AUC的指标的原因。

clipboard.png

或者我们也可以从另一个角度考虑:条件概率。我们假设X为预测值,Y为真实值。那么就可以将这些指标按条件概率表示:

精准率 = P(Y=1 | X=1)

召回率 = 灵敏度 = P(X=1 | Y=1)

特异度 = P(X=0 | Y=0)

从上面三个公式看到:如果我们先以实际结果为条件(召回率,特异度),那么就只需考虑一种样本,而先以预测值为条件(精准率),那么我们需要同时考虑正样本和负样本。所以先以实际结果为条件的指标都不受样本不平衡的影响,相反以预测结果为条件的就会受到影响。

2. ROC(接受者操作特征曲线)

ROC(Receiver Operating Characteristic)曲线,又称接受者操作特征曲线。该曲线最早应用于雷达信号检测领域,用于区分信号与噪声。后来人们将其用于评价模型的预测能力,ROC曲线是基于混淆矩阵得出的。

ROC曲线中的主要两个指标就是真正率和假正率,上面也解释了这么选择的好处所在。其中横坐标为假正率(FPR),纵坐标为真正率(TPR),下面就是一个标准的ROC曲线图。

clipboard.png

ROC曲线的阈值问题

与前面的P-R曲线类似,ROC曲线也是通过遍历所有阈值来绘制整条曲线的。如果我们不断的遍历所有阈值,预测的正样本和负样本是在不断变化的,相应的在ROC曲线图中也会沿着曲线滑动。

clipboard.png

如何判断ROC曲线的好坏?

改变阈值只是不断地改变预测的正负样本数,即TPR和FPR,但是曲线本身是不会变的。那么如何判断一个模型的ROC曲线是好的呢?这个还是要回归到我们的目的:FPR表示模型虚报的响应程度,而TPR表示模型预测响应的覆盖程度。我们所希望的当然是:虚报的越少越好,覆盖的越多越好。所以总结一下就是TPR越高,同时FPR越低(即ROC曲线越陡),那么模型的性能就越好。参考如下动态图进行理解。

clipboard.png

ROC曲线无视样本不平衡

前面已经对ROC曲线为什么可以无视样本不平衡做了解释,下面我们用动态图的形式再次展示一下它是如何工作的。我们发现:无论红蓝色样本比例如何改变,ROC曲线都没有影响。

clipboard.png

3. AUC(曲线下的面积)

为了计算 ROC 曲线上的点,我们可以使用不同的分类阈值多次评估逻辑回归模型,但这样做效率非常低。幸运的是,有一种基于排序的高效算法可以为我们提供此类信息,这种算法称为曲线下面积(Area Under Curve)

比较有意思的是,如果我们连接对角线,它的面积正好是0.5。对角线的实际含义是:随机判断响应与不响应,正负样本覆盖率应该都是50%,表示随机效果。ROC曲线越陡越好,所以理想值就是1,一个正方形,而最差的随机判断都有0.5,所以一般AUC的值是介于0.5到1之间的。

AUC的一般判断标准

0.5 - 0.7:效果较低,但用于预测股票已经很不错了

0.7 - 0.85:效果一般

0.85 - 0.95:效果很好

0.95 - 1:效果非常好,但一般不太可能

AUC的物理意义

曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。以下面的样本为例,逻辑回归预测从左到右以升序排列:

clipboard.png

ROC/AUC的Python实现

Python中我们可以调用sklearn机器学习库的metrics进行ROC和AUC的实现,简单的代码实现部分如下:

from sklearn import metrics
from sklearn.metrics import auc 
import numpy as np
y = np.array([1, 1, 2, 2])  
scores = np.array([0.1, 0.4, 0.35, 0.8])  
fpr, tpr, thresholds = metrics.roc_curve(y, scores, pos_label=2)
metrics.auc(fpr, tpr) 

0.75

以上就是所有关于ROC和AUC的讲解和实现,auc面积是0.75。如今的我再去面试,最希望面试官问我这个问题了,希望看过的朋友也可以彻底理解和记住ROC/AUC,以及各种指标率的概念。

参考:

1.机器学习,周志华
2.Python数据科学技术详解与商业实践,常国珍
3.https://developers.google.com...
4.https://lukeoakdenrayner.word...

关注微信公众号:Python数据科学,发现更多精彩内容。

图片描述

查看原文

赞 10 收藏 7 评论 1