Python Third Party

numpy

indexing

indexing 操作指的是 x[obj] 这种形式获取参数, 其中 xnp.ndarray 对象, 注意: x[1, 2]x[(1, 2)] 是完全等价的, 只是语法趟, 按 obj 的不同类型, 可以区分为如下几类:

Basic Indexing

Basic Indexing 触发的条件如下:

np.newaxis is None  # True
Ellipsis is ...     # True

# Basic Indexing 总是返回一个 view
obj: Union[int, slice, Ellipsis, np.newaxis, List[Union[int, slice, Ellipsis, np.newaxis]]]

注意 Basic Indexing 的返回的结果总是原数据的一个 View, 这暗示了一个副作用:

x = np.arange(1000000)
y = x[1]
del x   # 不会释放 x, 只是不能使用 x 这个标识符

一些例子:

x = np.arange(24)
x.shape = (2, 3, 4)
x[:, np.newaxis, :, :, None].shape  # (2, 1, 3, 4, 1)
x[..., 1:2].shape  # (2, 3, 1), 只能最多出现一个 Ellipsis, 并且 1:2 这种写法会保留这个维度本身
x[..., 1].shape    # (2, 3)

# slice(i, j, k), 首先总是将 i 和 j 转为整数, 然后再区分 k 为正数还是负数, 但区间总是左开右闭: [i, j)
x[0, 0, -1:-3:-1]  # slice(i=-1, j=-3, k=-1), 首先将 i, j 转换为整数, 转换方式为: i = -1 + x.shape[2] = 3, j = -3 + x.shape[2] = 1
# 等价于 x[0, 0, 3:1:-1], 因此取出: [x[0, 0, 3], x[0, 0, 2]] 得到数组 [3, 2]

# 空数组情形
x[1:1, ...].shape   # (0, 3, 4)

Advanced Indexing【很复杂,待续】

触发条件:

Advanced indexing is triggered when the selection object, obj, is a non-tuple sequence object, an ndarray (of data type integer or bool), or a tuple with at least one sequence object or ndarray (of data type integer or bool). There are two types of advanced indexing: integer and Boolean.

如果 obj 本身是序列类型(但不是元组)或是数组(数据类型可以是bool或int), 或者 obj 是一个元组, 但元组至少有一个元素是序列类型或是数组(数据类型可以是bool或int)

Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view).

Advanced Indexing 总是返回复制

一些例子:

奇怪的 id

topk

save & load

pandas

pandas 的 apply 系列

apply: DataFrame的方法, 可指定axis,应用于行或列

args用于指定额外参数, 但这些参数对于每行或每列是相同

applymap: DataFrame的方法, 应用于每一个元素

方法名
原型
说明

applymap

DataFrame.applymap(self, func)

逐元素操作

apply

DataFrame.apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds)

按行或列操作

apply

Series.apply(self, func, convert_dtype=True, args(),**kwds)

逐元素操作

map

Series.map(self, arg, na_action=None)

替换或者逐元素操作

pandas 分组操作

官方指南

总体上, 从效果上来说分为三个步骤:

  • Splitting: 数据分组, 对应的 API 是 pd.DataFrame.groupby

  • Applying: 一般来说有如下几类, 更一般地, 可以使用 splitting 的结果调用 apply 函数

    • Aggregation: 分组后, 对每一组计算一个统计值

    • Transformation: 分组后, 对每一组分别应用于一个变换, 例如对于 A 分组, 将空值填充为 "A", 对 B 分组, 将空值填充为 "B"

    • Filtration: 分组后, 根据一些条件筛选分组内的数据或者筛选整个组地数据, 例如如果一个分组的行数小于 10, 则删除整个分组的数据; 每个分组都只取前 3 行.

  • Combining: 将 Applying 的各个分组的结果合并在一次做返回

涉及的 API:

  • Splitting: pd.DataFrame.groupby: 返回一个 pandas.api.typing.DataFrameGroupBy 对象, 此对象有 groups, get_groups 等基础方法/属性, 也具有下面的 Applying 步骤中的 agg/aggregation, transform, filter, apply 等方法

  • Applying:

    • Aggregation: 内置的方法例如: mean, std, 更一般地可以使用 agg/aggregation (这两个方法是一样的, agg 只是 short-hand 写法)

    • Transformation: 内置的方法例如: cumsum, 更一般地可以使用 transform

    • Filtration: 内置的方法例如: head, 用于取每组的前几行, 使用自定义函数可以用 filter, 但注意 filter 只能将整组全部去掉或保留

    • 上面 3 种都不满足时, 可使用 apply 函数

Splitting: groupby 与 pandas.api.typing.DataFrameGroupBy 对象的方法

Applying: aggtransform, filter, apply

  • aggtransform, filter, apply 传参时的自定义函数 (UDF: User-Defined Function) 的输入输出条件 (官方文档似乎对此语焉不详) 不尽相同

  • 这几个方法最终 Combining 之后的 DataFrame 的 index 的形式有所不同, 这里只讨论 group(..., group_keys=True) 的情况:

    • apply 在 UDF 的出参是一个 DataFrame 的情况下, 会把 groupby 的列作为索引并保留原始索引以构成两级索引

    • transform 会把 groupby 的列丢弃, 原本的索引依然作为索引

    • agg 会把 groupby 的列作为索引, 原本索引丢弃

    • filter 除去索引可能会变少外, groupby 列被保留为列

太长不看系列 (transform, agg, filter 都可用 apply 实现):

参考资料:

UDF 的输入输出对比

transform

输入: 每组的每一列(Series)作为输入 输出: 与输入同等长度的列表/Series

agg

输入: 每组的每一列(Series)作为输入 输出: 标量

filter 输入: 每组的DataFrame作为输入 输出: True/False

apply 输入: 每组的DataFrame作为输入 输出: 标量/Series(不必与输入同等长度)/DataFrame

为了弄清这些函数的 UDF 的输入输出, 可以构造类似如下的测试代码

输出结果如下:

pandas 可能会自动做些优化 (与numba有关), 所以有些像上面那种测试代码可能会有比较诡异的结果, 例如:

pandas读写excel文件

参考链接1, 参考链接2

pandas读写excel依赖xlrd, xlwt包, (ps: 可以尝试直接使用这两个包直接进行读写excel文件)

直接使用xlrd包示例: 参考链接

直接使用xlwt包示例: 参考链接

某些情况下.xlsx被自动转为了.xlsm格式, 可以用pandas进行修复, 注意下面的例子也演示了如何获取一个excel文档的所有sheet名称

pandas index相关的操作

merge, join技巧

easydict/addict/dotmap

这几个包均是对 python 字典这一基本数据类型的封装,使得字典的属性可以使用点来访问,具体用法及区别待补充:

一些开源项目对这些包的使用情况:

语言检测模块

langdetect, langid

发送邮件模块

未仔细校对过

压缩与解压模块

zipfile模块

参考链接: https://www.datacamp.com/community/tutorials/zip-file#EZWP

pyhanlp

安装说明(1.7.8版本)

step 1

首先安装JPype1==0.7.0(版本号必须完全一致)

step 2

接下来安装pyhanlp(直接去网站下载代码pyhanlp-master.zip, 注意项目名为pyhanlp)

并下载: jar与配置文件hanlp-1.7.8-release.zip, 数据文件data-for-1.7.5.zip

注意data1.7.5是被1.7.8版本hanlp兼容的(实际上也没有data1.7.5版本), 至此原料已经准备齐全

首先将pyhanlp-master.zip解压, 并进入该目录用如下方式安装

接下来进入安装位置例如:

C:\Users\54120\anaconda3\envs\hanlp_copy\Lib\site-packages\pyhanlp-0.1.66-py3.7.egg\pyhanlp\static

data-for-1.7.5.zip解压后的data文件夹, hanlp-1.7.8-release.zip解压后的hanlp-1.7.8-sources.jar, hanlp-1.7.8.jar, hanlp.properties都放入上述目录下, 最终此目录的结构为:

step 3

修改hanlp.properties文件的内容

step 4

检查, 在命令行输入

另外, python中应该也要确保可以正常导入

注意

上述繁琐的过程使得环境迁移时除了拷贝envs还要修改配置文件.

spacy

下载模型

解决类似如下命令因为网络原因失效的方法:

https://github.com/explosion/spacy-models/查看相应的版本号, 下载类似如下链接的文件

更为详细的解释如下(spacy2.1.9版本源码分析)

执行 python -m spacy download en_core_web_sm 实际调用 site-packages/spacy/__main__.py。之后调用了

huggingface transformers

基本使用

模型下载目录

设置模型下载位置可参见官网介绍, 摘抄如下:

Caching models

This library provides pretrained models that will be downloaded and cached locally. Unless you specify a location with cache_dir=... when you use methods like from_pretrained, these models will automatically be downloaded in the folder given by the shell environment variable TRANSFORMERS_CACHE. The default value for it will be the Hugging Face cache home followed by /transformers/. This is (by order of priority):

  • shell environment variable HF_HOME

  • shell environment variable XDG_CACHE_HOME + /huggingface/

  • default: ~/.cache/huggingface/

So if you don’t have any specific environment variable set, the cache directory will be at ~/.cache/huggingface/transformers/.

Note: If you have set a shell environment variable for one of the predecessors of this library (PYTORCH_TRANSFORMERS_CACHE or PYTORCH_PRETRAINED_BERT_CACHE), those will be used if there is no shell environment variable for TRANSFORMERS_CACHE.

开源模型

发现有英翻中的模型, 开源模型目录, 搜索zh, (https://huggingface.co/Helsinki-NLP/opus-mt-en-zh)

使用方法:

离线模型下载实例

EncoderClassifier 中有如下注释:

离线下载模型步骤如下:

这样便可以直接使用如下方式导入模型(完全绕过默认路径 ~/.cache/huggingface/hub

备注:此处的 git clone 这一方法在离线下载时具有通用性,而修改 pretrain_pathspeechbrain 包的内部的逻辑造成的。如果不修改 pretrain_path,将无法绕过默认下载路径 ~/.cache/huggingface/hub

读写excel(xlsxwriter与pandas)

pandas与xlsxwriter均支持给输出的excel自定义格式.

对单元格的一些字符变成红色字符, 另一些字符仍然为黑色

数据验证(单元格内的值必须满足一定的条件)

html转pdf的(pdfkit)

依赖于wkhtmltopdf, 安装后(windows上需添加至环境变量)可以利用pdfkit包进行html到pdf的转换, 实际体验感觉对公式显示的支持不太好.

black(自动将代码规范化)

black模块可以自动将代码规范化(基本按照PEP8规范), 是一个常用工具

albumentations(待补充)

基于opencv的数据增强包

natsort

yacs

作者为 faster rcnn 的作者 Ross Girshick,用于解析 yaml 文件

timeout_decorator

超时自动退出装饰器

redis

python调用redis服务主要是如下两个第三方包:

  • 包名(pip install): redis, import时的模块名: redis

  • 包名(pip install): redis-py-cluster, import时的模块名: rediscluster

以上两个包存在一些版本兼容性问题:

  • redis-py-cluster依赖于redis, 但目前redis已经集成了redis-py-cluster的所有内容, 使用方式为:

    redis-py-cluster github README

    In the upstream package redis-py that this librar extends, they have since version * 4.1.0 (Dec 26, 2021) ported in this code base into the main branch.

    redis-py readthdocs

    The cluster client is based on Grokzen’s redis-py-cluster, has added bug fixes, and now supersedes that library. Support for these changes is thanks to his contributions

  • redis包在旧版本中存在RedisStrictRedis两个类, 但在目前的 3.x 及以上版本已经合并为一个类, 源码中有如下代码:

    关于StrictRedis与Redis的讨论参考stackoverflow

  • 结论: 不要使用redis-py-cluster, 直接安装redis 4.x及以上版本, 使用redis.Redisredis.cluster.RedisCluster类, 不要使用redis.StrictRedis

具体使用方式为:

pytest

经过阅读多篇相关的博客, 总结如下, python 包的项目组织形式严格按照如下方式进行

备注: 很多项目其实未必采用了“正确”的方式组织代码,“正确”的含义也会随着时间的推移而改变

关于安装

关于 pyproject.tomlsetup.pysetup.cfg:这三个文件与 pip install -e .pip install 的行为相关。

  • pyproject.toml:可以配置打包工具,也可以配置包的一些基本信息。

    • 当打包工具项配置为 setuptools.build_meta 时,那么 pip 会去按照 setup.py 的逻辑去执行安装命令,pyproject.tomlsetup.cfg 的其余配置项也会被自动用于 setup.py 的执行逻辑里

    • 当打包工具配置为其他选项例如:hatchling.build 时,那么 pip 会忽略 setup.py,而按照 pyproject.tomlsetup.cfg 的其余配置项执行安装命令。

  • setup.pypyproject.toml 同时存在时,执行 pip install . 命令时会按照 pyproject.toml 来执行。当然,执行 python setup.py install 安装时则忽略 pyproject.toml。但一般情况下,推荐使用 pip install . 而非 python setup.py install

关于测试

关于 __import__importimportlib.import_modulestackoverflow问答

import 实际上最终是调用了 __import__,而 __import__ 在底层是直接使用了 C 代码来实现。importlib.import_module 本质上的行为跟 __import__ 比较类似,只是实现方式上是使用纯 Python 来实现的。具体解释如下:

  • import tests.test_a.test_bsys.modules 增加 teststests.test_atests.test_a.test_b 这几个模块,当前文件可以使用 tests.test_a.test_b 这个命名空间,即可以使用 tests.test_a.test_b.xx

  • mod = importlib.import_module("tests.test_a.test_b")sys.modules 增加 teststests.test_atests.test_a.b 这几个模块,当前文件可以使用 mod 这个命名空间,即可以使用 mod.xx

  • 在进行了上面两种方式之一进行导入后,可以直接使用 sys.modules 获取 tests.test_a

pytest 在处理 import 的问题时, 支持了三种方式 prepend, appendimportlib。但每种方式都有各自的缺点。目前的最佳实践是:

  • 包放在 src 目录下,所有的模块各个目录应显式地添加 __init__.py。放入 src 目录最主要的目的是使得本地开发环境与 release 到 PyPI 后别人使用 pip install 的方式安装的环境相同。

  • 测试代码完全按包的形式组织,即各个目录显式地添加 __init__.py,独立于包之外,即与 src 目录同级方式 tests 文件夹。

  • 测试的目的是包在安装之后的行为是否正常,测试前应该以 pip install -e .pip install . 的方式将包安装。这也是包要放在 src 目录下的原因,即保证不会意外导入当前路径下的代码(很多情况下,sys.path 变量会把当前路径添加至模块搜索路径,这样子可能会意外导入)。

  • 以默认的 prepend 作为 pytest 的导入方式。注意:按照 pytest 内部的逻辑,使用 prepend 作为导入方式,不可避免地会修改 sys.path,但测试代码完全按包的形式组织,已经可以尽可能小的避免了 sys.path 的修改,但好处是 tests 目录下的各个文件之间可以相互 import。import 的方式应为 import tests.xx.yy。然而使用 importlib 作为导入方式,测试文件之间无法进行相互 import,这是一个重要的缺点。

  • 执行 pytest 命令(即不要以 python -m pytest的方式启动):使用 python -m pytest 会将当前目录添加至 sys.path 目录,因此要避免使用。

代码片段

打印带颜色的文本

Last updated

Was this helpful?