分类目录归档:Python 数据分析

教你纯原生实现Redis简易客户端(绕过Qmt白名单)

教你纯原生Python实现Redis简易客户端

Redis 是我们在开发过程中经常会用到的内存数据库,尤其是在Python的第三方模块Redis-py的支持下,在Python中使用Redis及其方便。

但是在有些情况下,我们无法使用像Redis-py这样的第三方模块(比如QMT),这时候就需要自己实现一个简易版的Redis-py了。

本文将教大家如何用20行代码,制作一个简易版的Redis客户端,不过仅以GET命令为例,其他命令的用法也差不多。

1.准备

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

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

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

2.原理剖析

其实通过Redis GET返回的数据就是一些字符串,这些字符串的格式如下:

b'$466\r\n\x80\x04\x95\xc7\x01\x00\x00\x00\x00\x00\x00]\x94(\x8c\x06000957\x94\x8c\x06002031\x94\x8c\x06000899\x94\x8c\x06300339\x94\x8c\x06002090\x94\x8c\x06601016\x94\x8c\x06002547\x94\x8c\x06002863\x94\x8c\x06002591\x94\x8c\x06002514\x94\x8c\x06000629\x94\x8c\x06002204\x94\x8c\x06000544\x94\x8c\x06002374\x94\x8c\x06000821\x94\x8c\x06000625\x94\x8c\x06000158\x94\x8c\x06002703\x94\x8c\x06002866\x94\x8c\x06600686\x94\x8c\x06002796\x94\x8c\x06300598\x94\x8c\x06002101\x94\x8c\x06002454\x94\x8c\x06000970\x94\x8c\x06000631\x94\x8c\x06002121\x94\x8c\x06600348\x94\x8c\x06600996\x94\x8c\x06002080\x94\x8c\x06002194\x94\x8c\x06002466\x94\x8c\x06300663\x94\x8c\x06002616\x94\x8c\x06000665\x94\x8c\x06600992\x94\x8c\x06300750\x94\x8c\x06300059\x94\x8c\x06002047\x94\x8c\x06002997\x94\x8c\x06000521\x94\x8c\x06002594\x94\x8c\x06002261\x94\x8c\x06002125\x94\x8c\x06002085\x94\x8c\x06002168\x94\x8c\x06002665\x94\x8c\x06002523\x94\x8c\x06603067\x94\x8c\x06002432\x94e.\r\n'

可见其是一个bytes字符串,开头$xxx是此数据的长度,\r\n作为分割符,后面紧跟着的就是你的原始数据内容,最后才是\r\n作为结尾。

根据这个返回内容,我们就可以制作一个简易的客户端用于在无法引用第三方模块的环境中接收Redis信息。

3.编写简易Redis客户端

与Redis通信,我们只需要用Python原生的socket模块即可。

import socket
import pickle

REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务,指定主机和端口
s.connect((REDIS_HOST, REDIS_PORT))
s.close()

这样就与你的Redis服务器连接上了,接下来只需要向socket发送你的命令并receive即可获取对应的内容:

import socket
import pickle

REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务,指定主机和端口
s.connect((REDIS_HOST, REDIS_PORT))
# GET 某个 KEY 的内容
s.send("GET RQB_keys_20220719 \r\n".encode("utf-8"))
# 接收小于 1M 的数据
msg = s.recv(1024 * 1024) 
s.close()
print(msg)
# b'$466\r\n\x80\x04\x95\xc7\x01\x00\x00\x00\x00\x00\x00]\x94(\x8c\x06000957\x94\x8c\x06002031\x94\x8c\x06000899\x94\x8c\x06300339\x94\x8c\x06002090\x94\x8c\x06601016\x94\x8c\x06002547\x94\x8c\x06002863\x94\x8c\x06002591\x94\x8c\x06002514\x94\x8c\x06000629\x94\x8c\x06002204\x94\x8c\x06000544\x94\x8c\x06002374\x94\x8c\x06000821\x94\x8c\x06000625\x94\x8c\x06000158\x94\x8c\x06002703\x94\x8c\x06002866\x94\x8c\x06600686\x94\x8c\x06002796\x94\x8c\x06300598\x94\x8c\x06002101\x94\x8c\x06002454\x94\x8c\x06000970\x94\x8c\x06000631\x94\x8c\x06002121\x94\x8c\x06600348\x94\x8c\x06600996\x94\x8c\x06002080\x94\x8c\x06002194\x94\x8c\x06002466\x94\x8c\x06300663\x94\x8c\x06002616\x94\x8c\x06000665\x94\x8c\x06600992\x94\x8c\x06300750\x94\x8c\x06300059\x94\x8c\x06002047\x94\x8c\x06002997\x94\x8c\x06000521\x94\x8c\x06002594\x94\x8c\x06002261\x94\x8c\x06002125\x94\x8c\x06002085\x94\x8c\x06002168\x94\x8c\x06002665\x94\x8c\x06002523\x94\x8c\x06603067\x94\x8c\x06002432\x94e.\r\n'

请注意,recv里你设定的大小会直接占用内存,所以请设定一个适宜的数目,或者从返回值中的美元符后的数字判断你需要接收的数据大小。

比如第一次请求,你只接收1024个字节,拿到 $xxx 这个长度后,重新send一次命令,再 s.recv(xxx) 长度。

上述例子中得到的内容是redis的格式,我们需要把\r\n给去除掉,并只取中间的数据便是我们存入redis的原始数据。

import pickle
def get_msg(msg):
    msg_new = msg.split(b"\r\n")[1]
    msg = pickle.loads(msg_new)
    return msg

因为我的原始内容是pickle格式,因此我在取出原始数据后使用pickle.loads便能拿到我想要的内容,完整代码如下:

import socket
import pickle

REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务,指定主机和端口
s.connect((REDIS_HOST, REDIS_PORT))
# GET 某个 KEY 的内容
s.send("GET RQB_keys_20220719 \r\n".encode("utf-8"))
# 接收小于 1M 的数据
msg = s.recv(1024 * 1024) 
s.close()

def get_msg(msg):
    msg_new = msg.split(b"\r\n")[1]
    msg = pickle.loads(msg_new)
    return msg

print(get_msg(msg))

效果如下:

['000957', '002031', '000899', '300339', '002090', '601016', '002547', '002863', '002591', '002514', '000629', '002204', '000544', '002374', '000821', '000625', '000158', '002703', '002866', '600686', '002796', '300598', '002101', '002454', '000970', '000631', '002121', '600348', '600996', '002080', '002194', '002466', '300663', '002616', '000665', '600992', '300750', '300059', '002047', '002997', '000521', '002594', '002261', '002125', '002085', '002168', '002665', '002523', '603067', '002432']

在QMT等会限制第三方模块的软件中,使用这样的方式访问Redis,就不会再遇到白名单的限制了。

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

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

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


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

Polars 速度极快的数据处理第三方模块

Polars 是一个速度极快的 DataFrames 库。

它拥有以下特性:

1.多线程

2.强大的表达式API

3.查询优化

下面给大家简单介绍一下这个模块的使用方式。

1.准备

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

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

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

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

pip install polars

2.Polars 使用介绍

在初始化变量的时候,Polars用起来的方式和Pandas没有太大区别,下面我们定义一个初始变量,后面所有示例都使用这个变量:

import polars as pl
df = pl.DataFrame(
    {
        "A": [1, 2, 3, 4, 5],
        "fruits": ["banana", "banana", "apple", "apple", "banana"],
        "B": [5, 4, 3, 2, 1],
        "cars": ["beetle", "audi", "beetle", "beetle", "beetle"],
    }
)

选择需要展示的数据:

(df.select([
    pl.col("A"),
    "B",      # the col part is inferred
    pl.lit("B"),  # we must tell polars we mean the literal "B"
    pl.col("fruits"),
]))

效果如下:

他还能使用正则表达式筛选值并进行求和等操作:

# 正则表达式
(df.select([
    pl.col("^A|B$").sum()
]))
# 或者多选
(df.select([
    pl.col(["A", "B"]).sum()
]))

Polars支持下面这样复杂且高效的查询及展示:

>>> df.sort("fruits").select(
...     [
...         "fruits",
...         "cars",
...         pl.lit("fruits").alias("literal_string_fruits"),
...         pl.col("B").filter(pl.col("cars") == "beetle").sum(),
...         pl.col("A").filter(pl.col("B") > 2).sum().over("cars").alias("sum_A_by_cars"),
...         pl.col("A").sum().over("fruits").alias("sum_A_by_fruits"),
...         pl.col("A").reverse().over("fruits").alias("rev_A_by_fruits"),
...         pl.col("A").sort_by("B").over("fruits").alias("sort_A_by_B_by_fruits"),
...     ]
... )
shape: (5, 8)
┌──────────┬──────────┬──────────────┬─────┬─────────────┬─────────────┬─────────────┬─────────────┐
│ fruits   ┆ cars     ┆ literal_stri ┆ B   ┆ sum_A_by_ca ┆ sum_A_by_fr ┆ rev_A_by_fr ┆ sort_A_by_B │
│ ---      ┆ ---      ┆ ng_fruits    ┆ --- ┆ rs          ┆ uits        ┆ uits        ┆ _by_fruits  │
│ str      ┆ str      ┆ ---          ┆ i64 ┆ ---         ┆ ---         ┆ ---         ┆ ---         │
│          ┆          ┆ str          ┆     ┆ i64         ┆ i64         ┆ i64         ┆ i64         │
╞══════════╪══════════╪══════════════╪═════╪═════════════╪═════════════╪═════════════╪═════════════╡
│ "apple"  ┆ "beetle" ┆ "fruits"     ┆ 11  ┆ 4           ┆ 7           ┆ 4           ┆ 4           │
│ "apple"  ┆ "beetle" ┆ "fruits"     ┆ 11  ┆ 4           ┆ 7           ┆ 3           ┆ 3           │
│ "banana" ┆ "beetle" ┆ "fruits"     ┆ 11  ┆ 4           ┆ 8           ┆ 5           ┆ 5           │
│ "banana" ┆ "audi"   ┆ "fruits"     ┆ 11  ┆ 2           ┆ 8           ┆ 2           ┆ 2           │
│ "banana" ┆ "beetle" ┆ "fruits"     ┆ 11  ┆ 4           ┆ 8           ┆ 1           ┆ 1           │
└──────────┴──────────┴──────────────┴─────┴─────────────┴─────────────┴─────────────┴─────────────┘

3.Polars 高级使用

倒序操作,将值倒序后重新放回变量中,起名为xxx_reverse:

(df.select([
    pl.all(),
    pl.all().reverse().suffix("_reverse")
]))

对所有列求和,并放回变量中,起名为 xxx_sum:

(df.select([
    pl.all(),
    pl.all().sum().suffix("_sum")
]))

正则也能用于筛选:

predicate = pl.col("fruits").str.contains("^b.*")

(df.select([
    predicate
]))

在设定一个新列的时候,甚至可以根据条件来给不同的行设定值:

(df.select([
    "fruits",
    "B",
    pl.when(pl.col("fruits") == "banana").then(pl.col("B")).otherwise(-1).alias("b")
]))

fold 函数很强大,它能在列上执行操作,获得最快的速度,也就是矢量化执行:

df = pl.DataFrame({
        "a": [1, 2, 3],
        "b": [10, 20, 30],
    }
)

out = df.select(
    pl.fold(acc=pl.lit(0), f=lambda acc, x: acc + x, exprs=pl.col("*")).alias("sum"),
)
print(out)
#shape: (3, 1)
#┌─────┐
#│ sum │
#│ --- │
#│ i64 │
#╞═════╡
#│ 11  │
#├╌╌╌╌╌┤
#│ 22  │
#├╌╌╌╌╌┤
#│ 33  │
#└─────┘

Polars 还有许多其他有用的特性,大家感兴趣的可以访问他们的用户手册进行阅读和学习:

https://pola-rs.github.io/polars-book/user-guide

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

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

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


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

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实用宝典

TinyDB 一个纯Python编写的轻量级数据库

TinyDB 是一个纯 Python 编写的轻量级数据库,一共只有1800行代码,没有外部依赖项。目标是降低小型 Python 应用程序使用数据库的难度,对于一些简单程序而言与其用 SQL 数据库,不如就用TinyDB.

TinyDB的特点是:

  • 轻便:当前源代码有 1800 行代码(大约 40% 的文档)和 1600 行测试代码。
  • 可随意迁移:在当前文件夹下生成数据库文件,不需要任何服务,可以随意迁移。
  • 简单: TinyDB 通过提供简单干净的 API 使得用户易于使用。
  • 用纯 Python 编写: TinyDB 既不需要外部服务器,也不需要任何来自 PyPI 的依赖项。
  • 适用于 Python 3.6+ 和 PyPy3: TinyDB 适用于所有现代版本的 Python 和 PyPy。
  • 强大的可扩展性:您可以通过编写中间件修改存储的行为来轻松扩展 TinyDB。
  • 100% 测试覆盖率:无需解释。

1.准备

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

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

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

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

pip install tinydb

2.TinyDB 增删改查示例

初始化一个DB文件:

from tinydb import TinyDB
db = TinyDB('db.json')

这样就在当前文件夹下生成了一个名为 `db.json` 的数据库文件。

往里面插入数据

from tinydb import TinyDB
db = TinyDB('db.json')
db.insert({'type': 'apple', 'count': 7})
db.insert({'type': 'peach', 'count': 3})

可以看到,我们可以直接往数据库里插入字典数据,不需要任何处理。下面是批量插入的方法:

db.insert_multiple([
    {'name': 'John', 'age': 22},
    {'name': 'John', 'age': 37}])
db.insert_multiple({'int': 1, 'value': i} for i in range(2))

查询所有数据

from tinydb import TinyDB
db = TinyDB('db.json')
db.all()
# [{'count': 7, 'type': 'apple'}, {'count': 3, 'type': 'peach'}]

除了 .all() 我们还可以使用for循环遍历db:

from tinydb import TinyDB
db = TinyDB('db.json')
for item in db:
    print(item)
# {'count': 7, 'type': 'apple'}
# {'count': 3, 'type': 'peach'}

如果你需要搜索特定数据,可以使用Query():

from tinydb import TinyDB
db = TinyDB('db.json')
Fruit = Query()
db.search(Fruit.type == 'peach')
# [{'count': 3, 'type': 'peach'}]
db.search(Fruit.count > 5)
# [{'count': 7, 'type': 'apple'}]

更新数据:

from tinydb import TinyDB
db = TinyDB('db.json')
db.update({'foo': 'bar'})

# 删除某个Key
from tinydb.operations import delete
db.update(delete('key1'), User.name == 'John')

删除数据

删除数据也可以使用类似的条件语句:

from tinydb import TinyDB
db = TinyDB('db.json')
db.remove(Fruit.count < 5)
db.all()
# [{'count': 10, 'type': 'apple'}]

清空整个数据库:

from tinydb import TinyDB
db = TinyDB('db.json')
db.truncate()
db.all()
# []

3.高级查询

除了点操作符访问数据,你还可以用原生的dict访问表示法:

# 写法1
db.search(User.country-code == 'foo')
# 写法2
db.search(User['country-code'] == 'foo')

这两种写法是等效的。

另外在常见的查询运算符(==, <, >, …)之外,TinyDB还支持where语句:

from tinydb import where
db.search(where('field') == 'value')

这等同于:

db.search(Query()['field'] == 'value')

这种语法还能访问嵌套字段:

db.search(where('birthday').year == 1900)
# 或者
db.search(where('birthday')['year'] == 1900)

Any 查询方法:

db.search(Group.permissions.any(Permission.type == 'read'))
# [{'name': 'user', 'permissions': [{'type': 'read'}]},
# {'name': 'sudo', 'permissions': [{'type': 'read'}, {'type': 'sudo'}]},
# {'name': 'admin', 'permissions':
#        [{'type': 'read'}, {'type': 'write'}, {'type': 'sudo'}]}]

检查单个项目是否包含在列表中:

db.search(User.name.one_of(['jane', 'john']))

TinyDB还支持和Pandas类似的逻辑操作:

# Negate a query:
db.search(~ (User.name == 'John'))
# Logical AND:
db.search((User.name == 'John') & (User.age <= 30))
# Logical OR:
db.search((User.name == 'John') | (User.name == 'Bob'))

TinyDB的介绍就到这里,你还可以访问他们的官方文档,查看更多的使用方法:

https://tinydb.readthedocs.io/en/latest/usage.html

尤其是想基于TinyDB做些存储优化的同学,你们可以详细阅读 Storage & Middleware 章节。

我们的文章到此就结束啦,如果你喜欢今天的 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实用宝典

Cpca 这个Python神器能帮你自动识别文字中的省市区并绘图

在做NLP(自然语言处理)相关任务时,经常会遇到需要识别并提取省、城市、行政区的需求。虽然我们自己通过关键词表一个个查找也能实现提取目的,但是需要先搜集省市区关键词表,相对而言比较繁琐。

今天给大家介绍一个模块,你只需要把字符串传递给这个模块,他就能给你返回这个字符串内的省、市、区关键词,并能给你在图片上标注起来,它就是 Cpca 模块。

1.准备

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

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

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

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

pip install cpca

注意,目前 cpca 模块仅支持Python3及以上版本。

在 windows 上可能会出现类似如下问题

Building wheel for pyahocorasick (setup.py) ... error

先去下载 Microsoft Visual C++ Build Tools 安装VC++构建工具,再重新 pip install cpca,即可解决问题

2.Cpca 基本使用

通过两行代码就能实现最基本的省市区提取:

# 公众号: Python 实用宝典
# 2022/06/23

import cpca

location_str = [
    "广东省深圳市福田区巴丁街深南中路1025号新城大厦1层",
    "特斯拉上海超级工厂是特斯拉汽车首座美国本土以外的超级工厂,位于中华人民共和国上海市。",
    "三星堆遗址位于中国四川省广汉市城西三星堆镇的鸭子河畔,属青铜时代文化遗址"
]
df = cpca.transform(location_str)
print(df)

效果如下:

     省     市     区                     地址  adcode
0  广东省   深圳市   福田区     巴丁街深南中路1025号新城大厦1层  440304
1  上海市  None  None                      。  310000
2  四川省   德阳市   广汉市  城西三星堆镇的鸭子河畔,属青铜时代文化遗址  510681

注意第三条的广汉市,cpca 不仅识别到了语句中的县级市广汉市,还能自动匹配到其代管市的德阳市,不得不说非常强大。

如果你想获知程序是从字符串的那个位置提取出省市区名的,可以添加一个 pos_sensitive=True 参数:

# 公众号: Python 实用宝典
# 2022/06/23

import cpca

location_str = [
    "广东省深圳市福田区巴丁街深南中路1025号新城大厦1层",
    "特斯拉上海超级工厂是特斯拉汽车首座美国本土以外的超级工厂,位于中华人民共和国上海市。",
    "三星堆遗址位于中国四川省广汉市城西三星堆镇的鸭子河畔,属青铜时代文化遗址"
]
df = cpca.transform(location_str, pos_sensitive=True)
print(df)

效果如下:

(base) G:\push\20220623>python 1.py
     省     市     区                     地址  adcode  省_pos  市_pos  区_pos
0  广东省   深圳市   福田区     巴丁街深南中路1025号新城大厦1层  440304      0      3      6
1  上海市  None  None                      。  310000     38     -1     -1
2  四川省   德阳市   广汉市  城西三星堆镇的鸭子河畔,属青铜时代文化遗址  510681      9     -1     12

它标记出了识别到省、市、区的关键位置(index),当然如果是德阳市这种特殊的识别会被标记为-1.

3.高级使用

它还可以从大段文本中批量识别多个地区:

# 公众号: Python 实用宝典
# 2022/06/23

import cpca

long_text = "对一个城市的评价总会包含个人的感情。如果你喜欢一个城市,很有可能是喜欢彼时彼地的自己。"\
    "在广州、香港读过书,工作过,在深圳买过房、短暂生活过,去北京出了几次差。"\
    "想重点比较一下广州、深圳和香港,顺带说一下北京。总的来说,觉得广州舒适、"\
    "香港精致、深圳年轻气氛好、北京大气又粗糙。答主目前选择了广州。"
df = cpca.transform_text_with_addrs(long_text, pos_sensitive=True)
print(df)

效果如下:

(base) G:\push\20220623>python 1.py
          省     市     区 地址  adcode  省_pos  市_pos  区_pos
0       广东省   广州市  None     440100     -1     44     -1
1   香港特别行政区  None  None     810000     47     -1     -1
2       广东省   深圳市  None     440300     -1     58     -1
3       北京市  None  None     110000     71     -1     -1
4       广东省   广州市  None     440100     -1     86     -1
5       广东省   深圳市  None     440300     -1     89     -1
6   香港特别行政区  None  None     810000     92     -1     -1
7       北京市  None  None     110000    100     -1     -1
8       广东省   广州市  None     440100     -1    110     -1
9   香港特别行政区  None  None     810000    115     -1     -1
10      广东省   深圳市  None     440300     -1    120     -1
11      北京市  None  None     110000    128     -1     -1
12      广东省   广州市  None     440100     -1    143     -1

不仅如此,模块中还自带一些简单绘图工具,可以在地图上将上面输出的数据以热力图的形式画出来:

# 公众号: Python 实用宝典
# 2022/06/23

import cpca
from cpca import drawer

long_text = "对一个城市的评价总会包含个人的感情。如果你喜欢一个城市,很有可能是喜欢彼时彼地的自己。"\
    "在广州、香港读过书,工作过,在深圳买过房、短暂生活过,去北京出了几次差。"\
    "想重点比较一下广州、深圳和香港,顺带说一下北京。总的来说,觉得广州舒适、"\
    "香港精致、深圳年轻气氛好、北京大气又粗糙。答主目前选择了广州。"
df = cpca.transform_text_with_addrs(long_text, pos_sensitive=True)
drawer.draw_locations(df[cpca._ADCODE], "df.html")

运行的时候可能会报这个错:

(base) G:\push\20220623>python 1.py
Traceback (most recent call last):
  File "1.py", line 12, in <module>
    drawer.draw_locations(df[cpca._ADCODE], "df.html")
  File "G:\Anaconda3\lib\site-packages\cpca\drawer.py", line 41, in draw_locations
    import folium
ModuleNotFoundError: No module named 'folium'

使用pip安装即可:

pip install folium

然后重新运行代码,会在当前目录下生成 df.html, 双击打开,效果如下:

怎么用,是不是感觉非常方便?以后地点的识别用这个模块就完全够了。

还有更多的细节你可以访问这个项目的Github主页阅读,该项目的README完全中文编写,非常容易阅读:

https://github.com/DQinYuan/chinese_province_city_area_mapper

如果你无法访问GitHub,也可以在Python实用宝典公众号后台回复:cpca 下载完整项目。

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

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

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


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