标签归档:数据分析

Manim 一个特漂亮的Python数学教学动画开发模块

Manim 是3b1b开源的一个特别漂亮的数学动画模块。

我们能够基于Manim绘制许多解释性的动画,比如下面这个:

也支持函数图像:

甚至是一些3D视图和矩阵变换,Manim都可以轻易实现:

如果你是一个数学课程的演讲者,或者你需要给观众演示某些数学公式的图形,那么Manim就是你的不二之选。

Manim 支持 Python 3.7 及以上版本,推荐Python3.8.

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

conda create --name manim python=3.8 # 创建虚拟环境
conda activate manim # 切换到此虚拟环境
pip install manimgl # 安装manim

安装完毕后在终端输入 manimgl,会出现如下的界面,说明安装完成。

2. Manim 基本使用

首先学会画一个基本的数学图形,如圆圈:

from manimlib import *

class SquareToCircle(Scene):
    def construct(self):
        circle = Circle()
        circle.set_fill(BLUE, opacity=0.5)
        circle.set_stroke(BLUE_E, width=4)

        self.add(circle)

编写完毕后,在终端里敲下这行命令:

manimgl 你的py文件名.py SquareToCircle

就能弹出一个图形界面,绘制完成:

你还可以操作弹出的这个窗口:

  • 滚动鼠标中键来上下移动画面
  • 按住键盘上 z 键的同时滚动鼠标中键来缩放画面
  • 按住键盘上 f 键的同时移动鼠标来平移画面
  • 按住键盘上 d 键的同时移动鼠标来改变三维视角
  • 按下键盘上 r 键恢复到最初的视角

最后,你可以通过按 q 来关闭窗口并退出程序。

接下来,我们学习如何让圆形变成方形:

# 公众号: Python实用宝典
from manimlib import *

class CircleToSquare(Scene):
    def construct(self):
        square = Square()
        square.set_fill(BLUE, opacity=0.5)
        square.set_stroke(BLUE_E, width=4)
        circle = Circle()

        self.play(ShowCreation(circle))
        self.wait()
        self.play(ReplacementTransform(circle, square))
        self.wait()

ShowCreation: 演示圆圈绘制过程。

ReplacementTransform: 延时从第一个参数的图形变化到第二个参数的图形的过程。

self.wait(): 等待上个play操作执行完成。

终端运行命令:

manimgl 你的py文件名.py CircleToSquare

效果如下:

再来一个复杂一点的演示,增加拉伸、旋转和变换:

# 公众号: Python实用宝典
from manimlib import *

class CircleToSquare(Scene):
    def construct(self):
        square = Square()
        square.set_fill(BLUE, opacity=0.5)
        square.set_stroke(BLUE_E, width=4)
        circle = Circle()

        self.play(ShowCreation(circle))
        self.wait()
        self.play(ReplacementTransform(circle, square))
        self.wait()
        # 在水平方向上拉伸到四倍
        self.play(square.animate.stretch(4, dim=0))
        self.wait()
        # 旋转90°
        self.play(Rotate(square, TAU / 4))
        self.wait()
        # 在向右移动2单位同时缩小为原来的1/4
        self.play(square.animate.shift(2 * RIGHT), square.animate.scale(0.25))
        self.wait()
        # 为了非线性变换,给square增加10段曲线(不会播放动画)
        square.insert_n_curves(10)
        # 给square上的所有点施加f(z)=z^2的复变换
        self.play(square.animate.apply_complex_function(lambda z: z**2))
        self.wait()

square.animate.stretch: 将图形拉伸第一个参数的倍数,第二个维度指明方向,dim=0为水平方向,dim=1为垂直方向。

square.animate.shift: 可以调整图形位置和大小。

square.animate.apply_complex_function: 增加函数复变换。

效果如下:

3. Manim 坐标轴与函数图像

想要实现函数图像绘制,我们需要先添加坐标轴:

# 公众号: Python实用宝典
from manimlib import *

class GraphExample(Scene):
    def construct(self):
        axes = Axes((-3, 10), (-1, 8))
        axes.add_coordinate_labels()

        self.play(Write(axes, lag_ratio=0.01, run_time=1))

运行以下命令显示坐标轴:

manimgl 你的py文件名.py GraphExample

坐标轴绘制完成后,就可以开始绘制图像了:

class GraphExample(Scene):
    def construct(self):
        axes = Axes((-3, 10), (-1, 8))
        axes.add_coordinate_labels()

        self.play(Write(axes, lag_ratio=0.01, run_time=1))

        # Axes.get_graph会返回传入方程的图像
        sin_graph = axes.get_graph(
            lambda x: 2 * math.sin(x),
            color=BLUE,
        )
        # 默认情况下,它在所有采样点(x, f(x))之间稍微平滑地插值
        # 但是,如果图形有棱角,可以将use_smoothing设为False
        relu_graph = axes.get_graph(
            lambda x: max(x, 0),
            use_smoothing=False,
            color=YELLOW,
        )
        # 对于不连续的函数,你可以指定间断点来让它不试图填补不连续的位置
        step_graph = axes.get_graph(
            lambda x: 2.0 if x > 3 else 1.0,
            discontinuities=[3],
            color=GREEN,
        )

        # Axes.get_graph_label可以接受字符串或者mobject。如果传入的是字符串
        # 那么将将其当作LaTeX表达式传入Tex中
        # 默认下,label将生成在图像的右侧,并且匹配图像的颜色
        sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
        relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
        step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)

        self.play(
            ShowCreation(sin_graph),
            FadeIn(sin_label, RIGHT),
        )
        self.wait(2)
        self.play(
            ReplacementTransform(sin_graph, relu_graph),
            FadeTransform(sin_label, relu_label),
        )
        self.wait()
        self.play(
            ReplacementTransform(relu_graph, step_graph),
            FadeTransform(relu_label, step_label),
        )
        self.wait()

        parabola = axes.get_graph(lambda x: 0.25 * x**2)
        parabola.set_stroke(BLUE)
        self.play(
            FadeOut(step_graph),
            FadeOut(step_label),
            ShowCreation(parabola)
        )
        self.wait()

        # 你可以使用Axes.input_to_graph_point(缩写Axes.i2gp)来找到图像上的一个点
        dot = Dot(color=RED)
        dot.move_to(axes.i2gp(2, parabola))
        self.play(FadeIn(dot, scale=0.5))

        # ValueTracker存储一个数值,可以帮助我们制作可变参数的动画
        # 通常使用updater或者f_always让其它mobject根据其中的数值来更新
        x_tracker = ValueTracker(2)
        f_always(
            dot.move_to,
            lambda: axes.i2gp(x_tracker.get_value(), parabola)
        )

        self.play(x_tracker.animate.set_value(4), run_time=3)
        self.play(x_tracker.animate.set_value(-2), run_time=3)
        self.wait()

在这份代码中,我们先绘制了Sinx的图像,通过 ReplacementTransform 和 FadeTransform 转换成 ReLu 函数,然后通过同样的步骤转换成了Step图像。最后实现点在曲线上的移动。

manimgl 你的py文件名.py GraphExample

效果如下:

如果在运行的时候你出现了这样的错误:

请下载安装MiKTex和dvisvgm.

MiKTex: https://miktex.org/download

Dvisvgm: https://dvisvgm.de/Downloads/

还有更多有趣的绘制案例,你可以在Manim官网上学习:

https://docs.manim.org.cn/getting_started/example_scenes.html

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Annoy 1个优秀的”邻近搜索”解决方案

Annoy是由 spotify 开源的一个Python第三方模块,它能用于搜索空间中给定查询点的近邻点。

此外,众所周知,Python由于GIL的存在,它的多线程最多只能用上一个CPU核的性能。如果你想要做性能优化,就必须用上多进程。

但是多进程存在一个问题,就是所有进程的变量都是独立的,B进程访问不到A进程的变量,因此Annoy为了解决这个问题,增加了一个静态索引保存功能,你可以在A进程中保存Annoy变量,在B进程中通过文件的形式访问这个变量。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install annoy

2.基本使用

Annoy使用起来非常简单,学习成本极低。比如我们随意生成1000个0,1之间的高斯分布点,将其加入到Annoy的索引,并保存为文件:

# 公众号:Python 实用宝典
from annoy import AnnoyIndex
import random

f = 40
t = AnnoyIndex(f, 'angular')  # 用于存储f维度向量
for i in range(1000):
    v = [random.gauss(0, 1) for z in range(f)]
    t.add_item(i, v)

t.build(10) # 10 棵树,查询时,树越多,精度越高。
t.save('test.ann')

这样,我们就完成了索引的创建及落地。Annoy 支持4种距离计算方式:

"angular""euclidean""manhattan""hamming",或"dot",即余弦距离、欧几里得距离、曼哈顿距离、汉明距离及点乘距离。

接下来我们可以新建一个进程访问这个索引:

from annoy import AnnoyIndex

f = 40
u = AnnoyIndex(f, 'angular')
u.load('test.ann') 
print(u.get_nns_by_item(1, 5))
# [1, 607, 672, 780, 625]

其中,u.get_nns_by_item(i, n, search_k=-1, include_distances=False)返回第 i 个item的n个最近邻的item。在查询期间,它将检索多达search_k(默认n_trees * n)个点。如果设置include_distancesTrue,它将返回一个包含两个列表的元组:第二个列表中包含所有对应的距离。

3.算法原理

构建索引:在数据集中随机选择两个点,用它们的中垂线来切分整个数据集。再随机从两个平面中各选出一个顶点,再用中垂线进行切分,于是两个平面变成了四个平面。以此类推形成一颗二叉树。当我们设定树的数量时,这个数量指的就是这样随机生成的二叉树的数量。所以每颗二叉树都是随机切分的。

查询方法
1. 将每一颗树的根节点插入优先队列;
2. 搜索优先队列中的每一颗二叉树,每一颗二叉树都可以得到最多 Top K 的候选集;
3. 删除重复的候选集;
4. 计算候选集与查询点的相似度或者距离;
5. 返回 Top K 的集合。

4.附录

下面是Annoy的所有函数方法:

  • AnnoyIndex(f, metric) 返回可读写的新索引,用于存储f维度向量。metric 可以是 "angular""euclidean""manhattan""hamming",或"dot"
  • a.add_item(i, v)用于给索引添加向量v,i 是指第 i 个向量。
  • a.build(n_trees)用于构建 n_trees 的森林。查询时,树越多,精度越高。在调用build后,无法再添加任何向量。
  • a.save(fn, prefault=False)将索引保存到磁盘。保存后,不能再添加任何向量。
  • a.load(fn, prefault=False)从磁盘加载索引。如果prefault设置为True,它将把整个文件预读到内存中。默认值为False。
  • a.unload() 释放索引。
  • a.get_nns_by_item(i, n, search_k=-1, include_distances=False)返回第 i 个item的 n 个最近邻的item。
  • a.get_nns_by_vector(v, n, search_k=-1, include_distances=False)与上面的相同,但按向量v查询。
  • a.get_item_vector(i)返回第i个向量。
  • a.get_distance(i, j)返回向量i和向量j之间的距离。
  • a.get_n_items() 返回索引中的向量数。
  • a.get_n_trees() 返回索引中的树的数量。
  • a.on_disk_build(fn) 用以在指定文件而不是RAM中建立索引(在添加向量之前执行,在建立之后无需保存)。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 教你用 Rows 快速操作csv文件

Rows 是一个专门用于操作表格的第三方Python模块。

只要通过 Rows 读取 csv 文件,她就能生成可以被计算的 Python 对象。

相比于 pandas 的 pd.read_csv, 我认为 Rows 的优势在于其易于理解的计算语法和各种方便的导出和转换语法。它能非常方便地提取pdf中的文字、将csv转换为sqlite文件、合并csv等,还能对csv文件执行sql语法,还是比较强大的。

当然,它的影响力肯定没有 Pandas 大,不过了解一下吧,技多不压身。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install rows

2.Rows 基本使用

通过下面这个小示例,你就能知道Rows的基本使用方法。

假设我们有这样的一个csv表格数据:

state,city,inhabitants,area
AC,Acrelândia,12538,1807.92
AC,Assis Brasil,6072,4974.18
AC,Brasiléia,21398,3916.5
AC,Bujari,8471,3034.87
AC,Capixaba,8798,1702.58
[...]
RJ,Angra dos Reis,169511,825.09
RJ,Aperibé,10213,94.64
RJ,Araruama,112008,638.02
RJ,Areal,11423,110.92
RJ,Armação dos Búzios,27560,70.28
[...]

如果我们想要找出 state 为 RJ 并且人口大于 500000 的城市,只需要这么做:

import rows

cities = rows.import_from_csv("data/brazilian-cities.csv")
rio_biggest_cities = [
    city for city in cities
    if city.state == "RJ" and city.inhabitants > 500000
]
for city in rio_biggest_cities:
    density = city.inhabitants / city.area
    print(f"{city.city} ({density:5.2f} ppl/km²)")

和 Pandas 很像,但是语法比 Pandas 简单,整个模块也比 Pandas 轻量。

如果你想要自己新建一个”表格”, 你可以这么写:

from collections import OrderedDict
from rows import fields, Table


country_fields = OrderedDict([
    ("name", fields.TextField),
    ("population", fields.IntegerField),
])

countries = Table(fields=country_fields)
countries.append({"name": "Argentina", "population": "45101781"})
countries.append({"name": "Brazil", "population": "212392717"})
countries.append({"name": "Colombia", "population": "49849818"})
countries.append({"name": "Ecuador", "population": "17100444"})
countries.append({"name": "Peru", "population": "32933835"})

然后你可以迭代它:

for country in countries:
    print(country)
# Result:
#     Row(name='Argentina', population=45101781)
#     Row(name='Brazil', population=212392717)
#     Row(name='Colombia', population=49849818)
#     Row(name='Ecuador', population=17100444)
#     Row(name='Peru', population=32933835)
# "Row" is a namedtuple created from `country_fields`

# We've added population as a string, the library automatically converted to
# integer so we can also sum:
countries_population = sum(country.population for country in countries)
print(countries_population)  # prints 357378595

还可以将此表导出为 CSV 或任何其他支持的格式:

# 公众号:Python实用宝典
import rows
rows.export_to_csv(countries, "some-LA-countries.csv")

# html
rows.export_to_html(legislators, "some-LA-countries.csv")

从字典导入到rows对象:

import rows

data = [
    {"name": "Argentina", "population": "45101781"},
    {"name": "Brazil", "population": "212392717"},
    {"name": "Colombia", "population": "49849818"},
    {"name": "Ecuador", "population": "17100444"},
    {"name": "Peru", "population": "32933835"},
    {"name": "Guyana", },  # Missing "population", will fill with `None`
]
table = rows.import_from_dicts(data)
print(table[-1])  # Can use indexes
# Result:
#     Row(name='Guyana', population=None)

3.命令行工具

除了写Python代码外,你还可以直接使用Rows的命令行工具,下面介绍几个可能会经常被用到的工具。

读取pdf文件内的文字并保存为文件:

# 需要提前安装: pip install rows[pdf]
URL="http://www.imprensaoficial.rr.gov.br/app/_edicoes/2018/01/doe-20180131.pdf"
rows pdf-to-text $URL result.txt  # 保存到文件 显示进度条
rows pdf-to-text --quiet $URL result.txt  # 保存到文件 不显示进度条
rows pdf-to-text --pages=1,2,3 $URL # 输出三页到终端
rows pdf-to-text --pages=1-3 $URL # 输出三页到终端(使用 - 范围符)

将csv转化为sqlite:

rows csv2sqlite \
     --dialect=excel \
     --input-encoding=latin1 \
     file1.csv file2.csv \
     result.sqlite

合并多个csv文件:

rows csv-merge \
     file1.csv file2.csv.bz2 file3.csv.xz \
     result.csv.gz

对csv执行sql搜索:

# needs: pip install rows[html]
rows query \
    "SELECT * FROM table1 WHERE inhabitants > 1000000" \
    data/brazilian-cities.csv \
    --output=data/result.html

其他更多功能,请见Rows官方文档:

http://turicas.info/rows

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Altair 一个漂亮易用的统计可视化库,甚至可拖动计数!

Altair 是一个基于Jupyter Notebook的强大可视化库。它提供了强大而简洁的可视化语法,使我们能够快速构建各种统计可视化图表。

通过下面10行代码,你就能创建一个可交互的散点图:

import altair as alt

from vega_datasets import data
cars = data.cars()

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin',
).interactive()

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install altair vega_datasets

2.Altair 基本使用

Altair 中的数据是围绕 Pandas Dataframe 构建的。

我们首先导入 Pandas 并创建一个简单的 DataFrame 以进行可视化,a 列中有一个分类变量,b 列有一个数值变量:

import pandas as pd
data = pd.DataFrame({'a': list('CCCDDDEEE'),
                     'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]})

Altair 中的基本对象是Chart,它将上述的数据作为单个参数:

import altair as alt
chart = alt.Chart(data)

到目前为止,我们已经定义了 Chart 对象,但是我们还没有告诉图表对数据任何事情。接下来会出现。

有了这个图表对象,我们现在可以指定我们希望如何可视化数据,比如作为点:

alt.Chart(data).mark_point()

然后对数据进行编码,比如指定 a 列为x,b列为y:

alt.Chart(data).mark_point().encode(
    x='a', y='b'
)

效果如下:

如果你希望聚合求得某列得平均值,你还可以这么做:

alt.Chart(data).mark_point().encode(
    x='a',
    y='average(b)'
)

如果你希望使用柱状图,只需要把mark_point改为mark_bar:

alt.Chart(data).mark_bar().encode(
    x='a',
    y='average(b)'
)

还可以获得水平柱状图,我们只需要把x和y对调一下:

alt.Chart(data).mark_bar().encode(
    y='a',
    x='average(b)'
)

除了点状图和柱状图,Altair 还支持几十种图表类型:

更多的图表类型请在官网查看:

https://altair-viz.github.io/gallery/index.html

3.高级使用

你可以给图表自定义你喜欢的颜色和对应的横坐标纵坐标标题:

alt.Chart(data).mark_bar(color='firebrick').encode(
    alt.Y('a', title='category'),
    alt.X('average(b)', title='avg(b) by category')
)

你还可以将图表保存为HTML:

chart = alt.Chart(data).mark_bar().encode(
    x='a',
    y='average(b)',
)
chart.save('chart.html')

如果你希望能够通过区间选择数据点并计数,你可以这么做:

import altair as alt
from vega_datasets import data

source = data.cars()

brush = alt.selection(type='interval')

points = alt.Chart(source).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color=alt.condition(brush, 'Origin', alt.value('lightgray'))
).add_selection(
    brush
)

bars = alt.Chart(source).mark_bar().encode(
    y='Origin',
    color='Origin',
    x='count(Origin)'
).transform_filter(
    brush
)

points & bars

跟牛逼的是,Altair还可以做多图表联动:

# 公众号:Python实用宝典 整合
import altair as alt
from vega_datasets import data

cars = data.cars.url
brush = alt.selection_interval()

chart = alt.Chart(cars).mark_point().encode(
    y='Horsepower:Q',
    color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).properties(
    width=250,
    height=250
).add_selection(
    brush
)

chart.encode(x='Acceleration:Q') | chart.encode(x='Miles_per_Gallon:Q')

左边圈起来的 Acceleration 数据点,右边会对应显示其 Miles_per_Gallon 数据点:

除了这些,Altair还有更多的交互功能,比如选择框拖动、比例绑定、自动响应、表达式选择等等,你可以阅读 Altair 官网学习并使用:

https://altair-viz.github.io/user_guide/interactions.html

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

AutoGrad 这个Python神器能够帮你自动计算函数斜率和梯度

AutoGrad 是一个老少皆宜的 Python 梯度计算模块。

对于初高中生而言,它可以用来轻易计算一条曲线在任意一个点上的斜率。

对于大学生、机器学习爱好者而言,你只需要传递给它Numpy这样的标准数据库下编写的损失函数,它就可以自动计算损失函数的导数(梯度)。

我们将从普通斜率计算开始,介绍到如何只使用它来实现一个逻辑回归模型。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install autograd

2.AutoGrad 计算斜率

对于初高中生同学而言,它可以用来轻松计算斜率,比如我编写一个斜率为0.5的直线函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def oneline(x):
    y = x/2
    return y

grad_oneline = grad(oneline)
print(grad_oneline(3.0))

运行代码,传入任意X值,你就能得到在该X值下的斜率:

(base) G:\push\20220724>python 1.py
0.5

由于这是一条直线,因此无论你传什么值,都只会得到0.5的结果。

那么让我们再试试一个tanh函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad

def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

此时你会获得 1.0 这个 x 在tanh上的曲线的斜率:

(base) G:\push\20220724>python 1.py
0.419974341614026

我们还可以绘制出tanh的斜率的变化的曲线:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

import matplotlib.pyplot as plt
from autograd import elementwise_grad as egrad
x = np.linspace(-7, 7, 200)
plt.plot(x, tanh(x), x, egrad(tanh)(x))
plt.show()

图中蓝色的线是tanh,橙色的线是tanh的斜率,你可以非常清晰明了地看到tanh的斜率的变化。非常便于学习和理解斜率概念。

3.实现一个逻辑回归模型

有了Autograd,我们甚至不需要借用scikit-learn就能实现一个回归模型:

逻辑回归的底层分类就是基于一个sigmoid函数:

import autograd.numpy as np
from autograd import grad

# Build a toy dataset.
inputs = np.array([[0.52, 1.12,  0.77],
                   [0.88, -1.08, 0.15],
                   [0.52, 0.06, -1.30],
                   [0.74, -2.49, 1.39]])
targets = np.array([True, True, False, True])

def sigmoid(x):
    return 0.5 * (np.tanh(x / 2.) + 1)

def logistic_predictions(weights, inputs):
    # Outputs probability of a label being true according to logistic model.
    return sigmoid(np.dot(inputs, weights))

从下面的损失函数可以看到,预测结果的好坏取决于weights的好坏,因此我们的问题转化为怎么优化这个 weights 变量:

def training_loss(weights):
    # Training loss is the negative log-likelihood of the training labels.
    preds = logistic_predictions(weights, inputs)
    label_probabilities = preds * targets + (1 - preds) * (1 - targets)
    return -np.sum(np.log(label_probabilities))

知道了优化目标后,又有Autograd这个工具,我们的问题便迎刃而解了,我们只需要让weights往损失函数不断下降的方向移动即可:

# Define a function that returns gradients of training loss using Autograd.
training_gradient_fun = grad(training_loss)

# Optimize weights using gradient descent.
weights = np.array([0.0, 0.0, 0.0])
print("Initial loss:", training_loss(weights))
for i in range(100):
    weights -= training_gradient_fun(weights) * 0.01

print("Trained loss:", training_loss(weights))

运行结果如下:

(base) G:\push\20220724>python regress.py
Initial loss: 2.772588722239781
Trained loss: 1.067270675787016

由此可见损失函数以及下降方式的重要性,损失函数不正确,你可能无法优化模型。损失下降幅度太单一或者太快,你可能会错过损失的最低点。

总而言之,AutoGrad是一个你用来优化模型的一个好工具,它可以给你提供更加直观的损失走势,进而让你有更多优化想象力。有兴趣的朋友还可以看官方的更多示例代码:

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Pandas TA 一个能帮助你迅速计算量化投资技术指标的神器

Pandas TA 是一个基于Pandas模块开发的,具有上百个技术指标和常用指标的开源模块。它包括但不限于能够绘制62种蜡烛形态(晨星、乌云、十字星、孕线等等)、130个技术指标,如移动平均线、macd、hma、布林带、obv、aron、squeeze等等各种指标。

下面就来讲一下这个量化投资神器的安装和使用方法,如果对你有帮助,记得点个赞和在看支持一下哦。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install pandas_ta

此外,如果你想使用上全部指标,你需要安装TA-Lib

pip install Ta-Lib

安装TA-Lib的时候可能会遇到没有VC++14.0的报错,这时候我们需要手动安装,在 https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 中下载你对应的Python版本的 TA-Lib whl文件。

下载完成后执行以下命令:

# 公众号:二七阿尔量化
pip install D:\path\TA_lib‑0.4.24‑cp38‑cp38‑win_amd64.whl

就能成功手动安装 Ta-lib

2.Pandas TA 基本使用

为了方便介绍使用方法,我下载了沪深300的分钟级数据,如果你需要本文的全部代码及数据,请在二七阿尔量化公众号后台回复:pandas_ta 下载。

首先看看我们的数据类型:

# 公众号:二七阿尔量化
import pandas as pd
import pandas_ta as ta

sh300data = pd.read_csv("sh300_1min.csv")

print(sh300data)

#        Unnamed: 0                  day      open      high       low     close     volume
# 0               0  2022-03-07 10:47:00  4406.223  4406.352  4405.662  4405.922   54345400
# 1               1  2022-03-07 10:48:00  4406.172  4406.175  4403.834  4403.918   70803100
# 2               2  2022-03-07 10:49:00  4403.333  4403.333  4402.235  4402.340   49632500
# 3               3  2022-03-07 10:50:00  4402.330  4402.519  4401.838  4402.519   48159200

默认情况下,pandas ta 取  open 、high、low、close、volumeadj_close 列作为指标参数,本文的列名正好对应的上,无需再去特别命名。

先试试收益率计算:

sh300data = sh300data.set_index("day")
print(sh300data.ta.log_return(cumulative=True, append=True))
# 2022-03-07 10:47:00    0.000000
# 2022-03-07 10:48:00   -0.000455
# 2022-03-07 10:49:00   -0.000813
# 2022-03-07 10:50:00   -0.000773
# 2022-03-07 10:51:00   -0.000826

验证一下结果:

>>> (- 4405.922 + 4403.918) / 4405.922
-0.00045484236897518966
>>> (- 4403.918 + 4402.34) / 4403.918
-0.00035831729836920665
>>> -0.00045484236897518966 + -0.00035831729836920665
-0.0008131596673443963

结果对得上,注意我们传递了cumulative=True参数,因此每次都会将前面的收益率累加。

试一下常用指标 sma:

sma10 = sh300data.ta.sma(length=10)
print(sma10)
# day
# 2022-03-07 10:47:00          NaN
# 2022-03-07 10:48:00          NaN
# 2022-03-07 10:49:00          NaN
# 2022-03-07 10:50:00          NaN
# 2022-03-07 10:51:00          NaN
#                          ...
# 2022-07-08 14:55:00    4429.3936
# 2022-07-08 14:56:00    4428.9421
# 2022-07-08 14:57:00    4428.5996
# 2022-07-08 14:58:00    4428.3280
# 2022-07-08 15:00:00    4428.1877
# Name: SMA_10, Length: 20000, dtype: float64

支持以下全部技术指标:aberration, above, above_value, accbands, ad, adosc, adx, alma, amat, ao, aobv, apo, aroon, atr, bbands, below, below_value, bias, bop, brar, cci, cdl_pattern, cdl_z, cfo, cg, chop, cksp, cmf, cmo, coppock, cross, cross_value, cti, decay, decreasing, dema, dm, donchian, dpo, ebsw, efi, ema, entropy, eom, er, eri, fisher, fwma, ha, hilo, hl2, hlc3, hma, hwc, hwma, ichimoku, increasing, inertia, jma, kama, kc, kdj, kst, kurtosis, kvo, linreg, log_return, long_run, macd, mad, massi, mcgd, median, mfi, midpoint, midprice, mom, natr, nvi, obv, ohlc4, pdist, percent_return, pgo, ppo, psar, psl, pvi, pvo, pvol, pvr, pvt, pwma, qqe, qstick, quantile, rma, roc, rsi, rsx, rvgi, rvi, short_run, sinwma, skew, slope, sma, smi, squeeze, squeeze_pro, ssf, stc, stdev, stoch, stochrsi, supertrend, swma, t3, td_seq, tema, thermo, tos_stdevall, trima, trix, true_range, tsi, tsignals, ttm_trend, ui, uo, variance, vhf, vidya, vortex, vp, vwap, vwma, wcp, willr, wma, xsignals, zscore

试一下十字星(Doji Candlestick)形态:

doji = sh300data.ta.cdl_pattern(name="doji")
print(doji)
#                      CDL_DOJI_10_0.1
# day
# 2022-03-07 10:47:00              0.0
# 2022-03-07 10:48:00              0.0
# 2022-03-07 10:49:00              0.0
# 2022-03-07 10:50:00              0.0
# 2022-03-07 10:51:00              0.0
# ...                              ...
# 2022-07-08 14:55:00              0.0
# 2022-07-08 14:56:00            100.0
# 2022-07-08 14:57:00              0.0
# 2022-07-08 14:58:00              0.0
# 2022-07-08 15:00:00              0.0

这里的值如果出现 100 ,就是出现DOJI形态。支持以下全部蜡烛形态:

2crows, 3blackcrows, 3inside, 3linestrike, 3outside, 3starsinsouth, 3whitesoldiers, abandonedbaby, advanceblock, belthold, breakaway, closingmarubozu, concealbabyswall, counterattack, darkcloudcover, doji, dojistar, dragonflydoji, engulfing, eveningdojistar, eveningstar, gapsidesidewhite, gravestonedoji, hammer, hangingman, harami, haramicross, highwave, hikkake, hikkakemod, homingpigeon, identical3crows, inneck, inside, invertedhammer, kicking, kickingbylength, ladderbottom, longleggeddoji, longline, marubozu, matchinglow, mathold, morningdojistar, morningstar, onneck, piercing, rickshawman, risefall3methods, separatinglines, shootingstar, shortline, spinningtop, stalledpattern, sticksandwich, takuri, tasukigap, thrusting, tristar, unique3river, upsidegap2crows, xsidegap3methods

由于62种蜡烛形态太多了,你可能需要一次性捞出来,Pandas TA也支持你这么做:

# 公众号:二七阿尔量化
import pandas as pd
import pandas_ta as ta

sh300data = pd.read_csv("sh300_1min.csv")
sh300data = sh300data.set_index("day")
all_candle = sh300data.ta.cdl_pattern(name="all")
print(all_candle)
#                      CDL_2CROWS  CDL_3BLACKCROWS  CDL_3INSIDE  ...  CDL_UNIQUE3RIVER  CDL_UPSIDEGAP2CROWS  CDL_XSIDEGAP3METHODS
# day                                                            ...
# 2022-03-07 10:47:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-03-07 10:48:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-03-07 10:49:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-03-07 10:50:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-03-07 10:51:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# ...                         ...              ...          ...  ...               ...                  ...                   ...      
# 2022-07-08 14:55:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-07-08 14:56:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-07-08 14:57:00         0.0              0.0        100.0  ...               0.0                  0.0                   0.0      
# 2022-07-08 14:58:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      
# 2022-07-08 15:00:00         0.0              0.0          0.0  ...               0.0                  0.0                   0.0      

# [20000 rows x 62 columns]

3.高级使用

Pandas TA 还支持策略的使用:

# 公众号:二七阿尔量化
import pandas as pd
import pandas_ta as ta
from multiprocessing import Process, freeze_support

sh300data = pd.read_csv("sh300_1min.csv")
sh300data = sh300data.set_index("day")
CustomStrategy = ta.Strategy(
    name="Momo and Volatility",
    description="SMA 50,200, BBANDS, RSI, MACD and Volume SMA 20",
    ta=[
        {"kind": "sma", "length": 50},
        {"kind": "sma", "length": 200},
        {"kind": "bbands", "length": 20},
        {"kind": "rsi"},
        {"kind": "macd", "fast": 8, "slow": 21},
        {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"},
    ]
)


if __name__ == '__main__':
    freeze_support()
    sh300data.ta.strategy(CustomStrategy)
    print(sh300data)
    #                          Unnamed: 0      open      high       low  ...  MACD_8_21_9  MACDh_8_21_9  MACDs_8_21_9  VOLUME_SMA_20
    # day                                                            ...
    # 2022-03-07 10:47:00           0  4406.223  4406.352  4405.662  ...          NaN           NaN           NaN            NaN        
    # 2022-03-07 10:48:00           1  4406.172  4406.175  4403.834  ...          NaN           NaN           NaN            NaN        
    # 2022-03-07 10:49:00           2  4403.333  4403.333  4402.235  ...          NaN           NaN           NaN            NaN        
    # 2022-03-07 10:50:00           3  4402.330  4402.519  4401.838  ...          NaN           NaN           NaN            NaN        
    # 2022-03-07 10:51:00           4  4402.376  4402.699  4402.129  ...          NaN           NaN           NaN            NaN        
    # ...                         ...       ...       ...       ...  ...          ...           ...           ...            ...        
    # 2022-07-08 14:55:00       19995  4428.123  4428.371  4427.098  ...    -1.700179     -0.244194     -1.455985     50578250.0        
    # 2022-07-08 14:56:00       19996  4427.209  4427.688  4426.886  ...    -1.725356     -0.215496     -1.509860     53128625.0        
    # 2022-07-08 14:57:00       19997  4427.279  4428.605  4427.279  ...    -1.583555     -0.058956     -1.524599     55393515.0        
    # 2022-07-08 14:58:00       19998  4428.268  4428.458  4428.268  ...    -1.426088      0.078808     -1.504897     53840375.0        
    # 2022-07-08 15:00:00       19999  4427.963  4428.781  4427.963  ...    -1.241029      0.211094     -1.452123     60235755.0        

    # [20000 rows x 18 columns]

可以看到,策略其实就是让你将一些技术指标提前配置好,通过调用策略能够自动将这些技术指标附加到你的数据集上,非常方便。

此外,策略计算的时候会用到多进程,多进程的并行数量也是可以控制的:

# 设置为4个核心,即最多4个并行
sh300data.ta.cores = 4

# 设置为0则不用多进程
sh300data.ta.cores = 0

# 查看并行数量
print(sh300data.ta.cores)
# 0

好啦,关于Pandas TA我们就先介绍到这里啦,如果你需要了解更多内容,可以访问官方文档:

https://github.com/twopirllc/pandas-ta

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python Pyintervals 解决你的阈值判断问题

Pyintervals 是一个用于数值区间计算的模块,比如我们想要判断一个数值是否处于一个、或者一系列区间范围内,就可以使用Pyintervals模块取缔IF-ELSE语句以达到简化代码的目的。

如果你想一次性生成上千个区间阈值并进行数值区间判断,比如根据数值生成成百上千个分类,那么这个模块就是你的最佳选择。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install pyinterval

2.Pyintervals 使用方法

使用Pyinterval做区间和阈值判断是非常简单的:

from interval import interval
a = interval[1,5]
# interval([1.0, 5.0])
print(3 in a)
# True

此外,你还可以构建一个多区间:

from interval import interval
a = interval([0, 1], [2, 3], [10, 15])
print(2.5 in a)
# True

interval.hall 方法还可以将多个区间合并,取其最小及最大值为边界:

from interval import interval
a = interval.hull((interval[1, 3], interval[10, 15], interval[16, 2222]))
# interval([1.0, 2222.0])
print(1231 in a)
# True

区间并集计算:

from interval import interval
a = interval.union([interval([1, 3], [4, 6]), interval([2, 5], 9)])
# interval([1.0, 6.0], [9.0])
print(5 in a)
# True
print(8 in a)
# False

3.生成多个阈值区间

如果你在做深度学习训练分类任务,你的分类数量比较多,达到了上百个,请不要傻傻地使用IF-ELSE, 下面教你使用四行代码生成上百个阈值区间。

假设你的值区间分布在0,1之间,每个阈值范围为0.005,并有正负两个方向。下面这4行代码就能非常简单地实现你想要的区间阈值:

from interval import interval
import numpy as np
threshold_list = np.arange(0.0, 1.0, 0.005)
intervals = [interval([threshold_list[i - 1], threshold_list[i]]) for i in range(1, len(threshold_list))]
intervals += [interval([-threshold_list[i], -threshold_list[i - 1]]) for i in range(len(threshold_list) - 1, 0, -1)]
print(len(intervals))
# 398
print(intervals[0], intervals[-1])
# interval([0.0, 0.005]) interval([-0.005, -0.0])

有了这个阈值,区间,你想要画分类就非常简单了,下面是一个简单示例,实际工作中要因不同应用场景改变使用方式。

target = 0.023
class_labels = {}
for index, interval_ in enumerate(intervals):
    if target in interval_:
        class_labels[target] = index

Pyintervals对于正在做大规模分类任务的同学而言是非常好用的模块,建议有需要的朋友可以试一试。其他同学也可以收藏点赞记录一下,说不定未来也会有应用场景呢!

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Tsmoothie 这个神奇的Python库,可以将数据平滑化并找到异常点

在处理数据的时候,我们经常会遇到一些非连续的散点时间序列数据:

有些时候,这样的散点数据是不利于我们进行数据的聚类和预测的。因此我们需要把它们平滑化,如下图所示:

将散点都去除,平滑后的效果如下:

​这样的时序数据是不是看起来舒服多了?​此外,使用平滑后的时序数据去做聚类或预测或许有令人惊艳的效果,因为它去除了一些偏差值并细化了数据的分布范围。

如果我们自己开发一个这样的平滑工具,会耗费不少的时间。​因为平滑的技术有很多种,你需要一个个地去研究,找到最合适的技术并编写代码,这是一个非常耗时的过程。平滑技术包括但不限于:

  • 指数平滑
  • 具有各种窗口类型(常数、汉宁、汉明、巴特利特、布莱克曼)的卷积平滑
  • 傅立叶变换的频谱平滑
  • 多项式平滑
  • 各种样条平滑(线性、三次、自然三次)
  • 高斯平滑
  • 二进制平滑

所幸,有大佬已经为我们实现好了时间序列的这些平滑技术,并在GitHub上开源了这份模块的代码——它就是 tsmoothie。

下面就让我们来试一下 tsmoothie.

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install tsmoothie

PS, Tsmoothie仅支持Python 3.6 及以上的版本。

2.Tsmoothie 基本使用

为了尝试Tsmoothie的效果,我们需要生成随机数据:

import numpy as np
import matplotlib.pyplot as plt
from tsmoothie.utils_func import sim_randomwalk
from tsmoothie.smoother import LowessSmoother

# 生成 3 个长度为200的随机数据组
np.random.seed(123)
data = sim_randomwalk(n_series=3, timesteps=200, 
                      process_noise=10, measure_noise=30)

然后使用Tsmoothie执行平滑化:

# 平滑
smoother = LowessSmoother(smooth_fraction=0.1, iterations=1)
smoother.smooth(data)

通过 smoother.smooth_data 你就可以获取平滑后的数据:

print(smoother.smooth_data)
# [[   5.21462928    3.07898076    0.93933646   -1.19847767   -3.32294934 
#     -5.40678762   -7.42425709   -9.36150892  -11.23591897  -13.05271523 
#      .......       .......       .......      .......       .......   ]]

绘制效果图:

# 生成范围区间
low, up = smoother.get_intervals('prediction_interval')

plt.figure(figsize=(18,5))

for i in range(3):
    
    plt.subplot(1,3,i+1)
    plt.plot(smoother.smooth_data[i], linewidth=3, color='blue')
    plt.plot(smoother.data[i], '.k')
    plt.title(f"timeseries {i+1}"); plt.xlabel('time')

    plt.fill_between(range(len(smoother.data[i])), low[i], up[i], alpha=0.3)

3.基于Tsmoothie的极端异常值检测

事实上,基于smoother生成的范围区域,我们可以进行异常值的检测:

可以看到,在蓝色范围以外的点,都属于异常值。我们可以轻易地将这些异常值标红或记录,以便后续的处理。

_low, _up = smoother.get_intervals('sigma_interval', n_sigma=2)
series['low'] = np.hstack([series['low'], _low[:,[-1]]])
series['up'] = np.hstack([series['up'], _up[:,[-1]]])
is_anomaly = np.logical_or(
    series['original'][:,-1] > series['up'][:,-1], 
    series['original'][:,-1] < series['low'][:,-1]
).reshape(-1,1)

假设蓝色范围interval的最大值为up、最小值为low,如果存在 data > up 或 data < low 则表明此数据是异常点。

使用以下代码通过滚动数据点进行平滑化和异常检测,就能保存得到上方的GIF动图。

# https://github.com/cerlymarco/MEDIUM_NoteBook/blob/master/Anomaly_Detection_RealTime/Anomaly_Detection_RealTime.ipynb

import numpy as np
import matplotlib.pyplot as plt
from celluloid import Camera
from collections import defaultdict
from functools import partial
from tqdm import tqdm

from tsmoothie.utils_func import sim_randomwalk, sim_seasonal_data
from tsmoothie.smoother import *


def plot_history(ax, i, is_anomaly, window_len, color='blue', **pltargs):
    
    posrange = np.arange(0,i)
    
    ax.fill_between(posrange[window_len:], 
                    pltargs['low'][1:], pltargs['up'][1:], 
                    color=color, alpha=0.2)
    if is_anomaly:
        ax.scatter(i-1, pltargs['original'][-1], c='red')
    else:
        ax.scatter(i-1, pltargs['original'][-1], c='black')
    ax.scatter(i-1, pltargs['smooth'][-1], c=color)
    
    ax.plot(posrange, pltargs['original'][1:], '.k')
    ax.plot(posrange[window_len:], 
            pltargs['smooth'][1:], color=color, linewidth=3)
    
    if 'ano_id' in pltargs.keys():
        if pltargs['ano_id'].sum()>0:
            not_zeros = pltargs['ano_id'][pltargs['ano_id']!=0] -1
            ax.scatter(not_zeros, pltargs['original'][1:][not_zeros], 
                       c='red', alpha=1.)

np.random.seed(42)

n_series, timesteps = 3, 200

data = sim_randomwalk(n_series=n_series, timesteps=timesteps, 
                      process_noise=10, measure_noise=30)

window_len = 20

fig = plt.figure(figsize=(18,10))
camera = Camera(fig)

axes = [plt.subplot(n_series,1,ax+1) for ax in range(n_series)]
series = defaultdict(partial(np.ndarray, shape=(n_series,1), dtype='float32'))

for i in tqdm(range(timesteps+1), total=(timesteps+1)):
    
    if i>window_len:
    
        smoother = ConvolutionSmoother(window_len=window_len, window_type='ones')
        smoother.smooth(series['original'][:,-window_len:])

        series['smooth'] = np.hstack([series['smooth'], smoother.smooth_data[:,[-1]]]) 

        _low, _up = smoother.get_intervals('sigma_interval', n_sigma=2)
        series['low'] = np.hstack([series['low'], _low[:,[-1]]])
        series['up'] = np.hstack([series['up'], _up[:,[-1]]])

        is_anomaly = np.logical_or(
            series['original'][:,-1] > series['up'][:,-1], 
            series['original'][:,-1] < series['low'][:,-1]
        ).reshape(-1,1)
        
        if is_anomaly.any():
            series['ano_id'] = np.hstack([series['ano_id'], is_anomaly*i]).astype(int)
            
        for s in range(n_series):
            pltargs = {k:v[s,:] for k,v in series.items()}
            plot_history(axes[s], i, is_anomaly[s], window_len, 
                         **pltargs)

        camera.snap()
        
    if i>=timesteps:
        continue
    
    series['original'] = np.hstack([series['original'], data[:,[i]]])

    
print('CREATING GIF...')  # it may take a few seconds
camera._photos = [camera._photos[-1]] + camera._photos
animation = camera.animate()
animation.save('animation1.gif', codec="gif", writer='imagemagick')
plt.close(fig)
print('DONE')

注意,异常点并非都是负面作用,在不同的应用场景下,它们可能代表了不同的意义。

比如在股票中,它或许可以代表着震荡行情中某种趋势反转的信号。

或者在家庭用电量分析中,它可能代表着某个时刻的用电峰值,根据这个峰值我们可以此时此刻开启了什么样的电器。

所以异常点的作用需要根据不同应用场景进行不同的分析,才能找到它真正的价值。

总而言之,Tsmoothie 不仅可以使用多种平滑技术平滑化我们的时序数据,还可以根据平滑结果找出数据中的离群点,是我们做数据分析和研究的一个好帮手,非常有价值。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

什么格式是保存 Pandas 数据的最好格式?

在数据分析相关项目工作时,我通常使用Jupyter笔记本和pandas库来处理和移动我的数据。对于中等大小的数据集来说,这是一个非常直接的过程,你甚至可以将其存储为纯文本文件而没有太多的开销。

然而,当你的数据集中的观测数据数量较多时,保存和加载数据回内存的过程就会变慢,现在程序的重新启动都会迫使你等待数据重新加载。所以最终,CSV文件或任何其他纯文本格式都会失去吸引力。

我们可以做得更好。有很多二进制格式可以用来将数据存储到磁盘上,其中有很多格式pandas都支持。我们怎么能知道哪一种更适合我们的目的呢?

来吧,我们尝试其中的几个,然后进行对比!这就是我决定在这篇文章中要做的:通过几种方法将 pandas.DataFrame 保存到磁盘上,看看哪一种在I/O速度、内存消耗和磁盘空间方面做的更好。

在这篇文章中,我将展示我的测试结果。

1.要比较的格式

我们将考虑采用以下格式来存储我们的数据:

1. CSV — 数据科学家的一个好朋友
2. Pickle — 一种Python的方式来序列化事物
3. MessagePack — 它就像JSON,但又快又小
4. HDF5 — 一种设计用于存储和组织大量数据的文件格式
5. Feather — 一种快速、轻量级、易于使用的二进制文件格式,用于存储数据框架
6. Parquet — Apache Hadoop的柱状存储格式

所有这些格式都是被广泛使用的,而且(也许除了MessagePack)在你做一些数据分析的事情时非常经常遇到。

为了追求找到最好的缓冲格式来存储程序会话之间的数据,我选择了以下指标进行比较。

1. size_mb – 文件大小(Mb)。
2. save_time – 将数据帧保存到磁盘上所需的时间量。
3. load_time – 将之前转储的数据帧加载到内存中所需要的时间量。
4. save_ram_delta_mb – 数据帧保存过程中最大的内存消耗增长量。
5. load_ram_delta_mb – 数据帧加载过程中的最大内存消耗增长量。

请注意,当我们使用高效压缩的二进制数据格式,如 Parquet 时,最后两个指标变得非常重要。它们可以帮助我们估计加载序列化数据所需的内存量,此外还有数据大小本身。我们将在接下来的章节中更详细地讨论这个问题。

2.测试及结果

我决定使用一个合成数据集进行测试,以便更好地控制序列化的数据结构和属性。

另外,我在我的基准中使用了两种不同的方法:

(a) 将生成的分类变量保留为字符串。

(b) 在执行任何I/O之前将它们转换为 pandas.Categorical 数据类型。

函数generate_dataset显示了我在基准中是如何生成数据集的:

def generate_dataset(n_rows, num_count, cat_count, max_nan=0.1, max_cat_size=100):
    """
    随机生成具有数字和分类特征的数据集。
    
    数字特征取自正态分布X ~ N(0, 1)。
    分类特征则被生成为随机的uuid4字符串。
    
    此外,数字和分类特征的max_nan比例被替换为NaN值。
    """
    dataset, types = {}, {}
    
    def generate_categories():
        from uuid import uuid4
        category_size = np.random.randint(2, max_cat_size)
        return [str(uuid4()) for _ in range(category_size)]
    
    for col in range(num_count):
        name = f'n{col}'
        values = np.random.normal(0, 1, n_rows)
        nan_cnt = np.random.randint(1, int(max_nan*n_rows))
        index = np.random.choice(n_rows, nan_cnt, replace=False)
        values[index] = np.nan
        dataset[name] = values
        types[name] = 'float32'
        
    for col in range(cat_count):
        name = f'c{col}'
        cats = generate_categories()
        values = np.array(np.random.choice(cats, n_rows, replace=True), dtype=object)
        nan_cnt = np.random.randint(1, int(max_nan*n_rows))
        index = np.random.choice(n_rows, nan_cnt, replace=False)
        values[index] = np.nan
        dataset[name] = values
        types[name] = 'object'
    
    return pd.DataFrame(dataset), types

我们将CSV文件的保存和加载性能作为一个基准。

五个随机生成的具有一百万个观测值的数据集被转储到CSV中,并读回内存以获得平均指标。

每种二进制格式都针对20个随机生成的具有相同行数的数据集进行测试。

这些数据集包括15个数字特征和15个分类特征。你可以在这个资源库中找到带有基准测试功能和所需的完整源代码:

https://github.com/devforfu/pandas-formats-benchmark

或在Python实用宝典后台回复 Pandas IO对比 ,下载完整代码。

(a) 数据为字符串特征时的性能

下图显示了每种数据格式的平均I/O时间。一个有趣的观察是,hdf显示出比csv更慢的加载速度,而其他二进制格式的表现明显更好。其中最令人印象深刻的是feather和parquet。

在保存数据和从磁盘上读取数据时,内存开销如何?

下一张图片告诉我们,hdf 的表现就不是那么好了。可以肯定的是,csv在保存/加载纯文本字符串时不需要太多的额外内存,而Feather和parquet则相当接近:

最后,让我们看看文件的大小。这次parquet显示了一个令人印象深刻的结果,考虑到这种格式是为有效存储大量数据而开发的,这并不令人惊讶。

(b) 字符串特征转换为数字时的性能

在上一节中,我们没有尝试有效地存储我们的分类特征而是使用普通的字符串。让我们来弥补这个遗漏吧! 这一次我们使用一个专门的 pandas.Categorical 类型,转字符串特征为数字特征。

看看现在与纯文本的csv相比,它看起来如何!

现在所有的二进制格式都显示出它们的真正力量。Csv的基准结果已经远远落后了,所以让我们把它去掉,以便更清楚地看到各种二进制格式之间的差异:

Feather 和 Pickle 显示了最好的 I/O 速度,而 hdf 仍然显示了明显的性能开销。

现在是时候比较数据进程加载时的内存消耗了。下面的柱状图显示了我们之前提到的关于parquet格式的一个重要事实。

可以看到 parquet 读写时的内存空间差距有多大,你有可能你无法将比较大的 parquet 文件加载到内存中。

最后的图显示了各格式的文件大小。所有的格式都显示出良好的效果,除了hdf仍然需要比其他格式多得多的空间:

3.结论

正如我们的测试所显示的,似乎 feather 格式是存储Python会话数据的理想候选者。它显示了很快的I/O速度,在磁盘上不占用太多内存,并且在加载回RAM时不需要消耗太大的内存。

当然,这种比较并不意味着你应该在每个可能的情况下使用这种格式。例如,feather格式一般不会被用作长期文件存储的格式。

另外,某些特定情况下也无法使用 feather,这由你的整个程序架构决定。然而,就如本帖开头所述的目的,它在不被任何特殊事项限制的情况下是一个很好的选择。

本文译自 towardsdatascience
作者: Ilia Zaitsev
有部分修改。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Sweetviz 让你三行代码实现数据分析

Sweetviz是一个开源Python库,它只需三行代码就可以生成漂亮的高精度可视化效果来启动EDA(探索性数据分析)。输出一个HTML。

如上图所示,它不仅能根据性别、年龄等不同栏目纵向分析数据,还能对每个栏目做众数、最大值、最小值等横向对比。

所有输入的数值、文本信息都会被自动检测,并进行数据分析、可视化和对比,最后自动帮你进行总结,是一个探索性数据分析的好帮手。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install sweetviz

2.sweetviz 基本用法

sweetviz 使用的原理是,使用一行代码,生成一个数据报告的对象(其中,my_dataframe是pandas中的DataFrame,一种表格型数据结构):

import pandas as pd
import sweetviz as sv

# 读取数据
my_dataframe = pd.read_csv('../ImpartData/iris.csv')
# 分析数据
my_report = sv.analyze(my_dataframe)
# 生成报告
my_report.show_html()

执行完成后,会在当前文件夹下生成一个HTML的报告文件

双击这个html,你就能看到精美的分析报告了:

其中,分析数据有三种函数可以用,除了上面提到的analyze函数,还有 compare 和 compare_intra 函数。

首先是analyze函数:

analyze(source: Union[pd.DataFrame, Tuple[pd.DataFrame, str]],
            target_feat: str = None,
            feat_cfg: FeatureConfig = None,
            pairwise_analysis: str = 'auto')

可见其有以下4个参数可以配置:

  • source:以pandas中的DataFrame数据结构作为分析对象。
  • target_feat:需要被标记为目标对象的字符串。
  • feat_cfg:需要被跳过、或是需要被强制转换为某种数据类型的特征。
  • pairwise_analysis:相关性分析可能需要花费较长时间。如果超过了你的忍受范围,就需要设置这个参数为on或者off,以判断是否需要分析数据相关性。

compare()丨两个数据集比较

my_report = sv.compare([my_dataframe, "Training Data"], [test_df, "Test Data"], "Survived", feature_config)

要比较两个数据集,只需使用该 compare() 函数。它的参数与 analyze() 相同,只是插入了第二个参数来覆盖比较数据帧。建议使用 [dataframe, “name”] 参数格式以更好地区分基础数据帧和比较数据帧。(例如 [my_df, "Train"] 比 my_df 更好)

compare_intra()丨数据集栏目比较

my_report = sv.compare_intra(my_dataframe, my_dataframe["Sex"] == "male", ["Male", "Female"], feature_config)

想要对数据集中某个栏目下的参数进行分析,就采用这个函数进行。
例如,如果需要比较“性别”栏目下的“男性”和“女性”,就可以采用这个函数。

3.调整报告布局

一旦你创建了你的报告对象,只需将它传递给两个show函数中的一个:

1. show_html():

show_html(  filepath='SWEETVIZ_REPORT.html', 
            open_browser=True, 
            layout='widescreen', 
            scale=None)

show_html(…)将在当前文件路径中创建并保存 HTML 报告。有以下参数:

  • layout (布局):无论是'widescreen''vertical'。当鼠标移过每个功能时,宽屏布局会在屏幕右侧显示详细信息。新的(从 2.0 开始)垂直布局在水平方向上更加紧凑,并且可以在单击时扩展每个细节区域。
  • scale:使用浮点数(scale= 0.8None)来缩放整个报告。
  • open_browser:启用 Web 浏览器的自动打开以显示报告。如果不需要,可以在此处禁用它。

2.show_notebook():

show_notebook(  w=None, 
                h=None, 
                scale=None,
                layout='widescreen',
                filepath=None)

它将嵌入一个 IFRAME 元素,在notebook中显示报告(例如 Jupyter、Google Colab 等)。

请注意,由于Notebook通常是一个更受限制的环境,因此使用自定义宽度/高度/比例值 ( whscale) 可能是个好主意。选项是:

  • w(宽度):设置报告输出窗口的宽度。可以是百分比字符串 ( w="100%") 或像素 (w=900)。
  • h(高度):设置报告输出窗口的高度。可以是像素数 ( h=700) 或将窗口拉伸到与所有特征 ( h="Full")一样高。
  • scale:与上面的 show_html 相同。
  • layout:与上面的 show_html 相同。
  • scale:与上面的 show_html 相同。
  • filepath:可选的输出 HTML 报告。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典