python中稀疏矩阵的常用表示COO LIL CSR CSC【上篇】
前言
sklearn调用独热编码函数encoder.fit_transform()返回的是scipy.sparse._csr.csr_matrix类型。
torch的tensor也是一种matrix,各中差别让我感觉混乱。今天梳理一下,网上看到前辈有很好的帖子,翻译转载如下:
简介
稀疏矩阵是大多数元素的值为0的矩阵。如果Number of Non-Zero (NNZ)非零元素个数的占比小于0.5,则矩阵是稀疏的。
存储所有0元素是低效的,因此我们假设未声明的元素都是0。利用这种方法,稀疏矩阵可以比相应的密集矩阵表示方法执行更快的操作,并且使用更少的内存,这在处理数据科学中的大数据集时尤为重要。
今天,我们将研究 SciPy sparse package提供的所有不同实现。这个实现是模仿 np.Matrix 而不是 np.ndarray,因此仅限于2维数组,并且np.matrix里的像 A* B这样的矩阵乘法而不是orch.Tensor里的逐元素相乘
Matrix multiplication(矩阵乘法)和element-wise multiplication(逐元素乘法)是两种不同的矩阵运算。
Matrix multiplication是两个矩阵相乘得到一个新的矩阵,其中第一个矩阵的列数必须等于第二个矩阵的行数。矩阵乘法可以表示为 C = A B C = AB C=AB,其中 A A A和 B B B是两个矩阵, C C C是乘积矩阵。在矩阵乘法中,每个元素的计算都涉及到两个矩阵中的多个元素的加权和,因此这种运算通常会涉及到高复杂度的计算。【np.matrx使用的就是矩阵乘法】
Element-wise multiplication是两个矩阵中的对应元素相乘得到一个新的矩阵,其中两个矩阵必须具有相同的形状。元素乘积可以表示为 C = A ⊙ B C = A \odot B C=A⊙B,其中 A A A和 B B B是两个矩阵, C C C是元素乘积矩阵。在逐元素乘法中,矩阵中的每个元素都是独立计算的,因此计算的复杂度要比矩阵乘法低得多。【而torch.Tensor使用的就是逐元素相乘】
n [0]: from scipy import sparseIn [1]: import numpy as npIn [2]: spmatrix = sparse.random(10, 10)In [3]: spmatrix
Out[3]:
<10x10 sparse matrix of type '<class 'numpy.float64'>'with 1 stored elements in COOrdinate format>In [4]: spmatrix.nnz / np.product(spmatrix.shape) # sparsity
Out[4]: 0.01
构造矩阵
不同的稀疏矩阵格式各有优缺点。一个好的起点是寻找对构造这些矩阵有效的格式。一般来说你都是从其中一种格式开始,然后转换成另一种格式用于计算。
坐标矩阵(Coordinate Matrix)
要理解的最简单的稀疏格式可能是 COO 格式(Coordinate Matrix)。此变体使用三个子数组来存储元素值及其坐标位置。
In [5]: row = [1, 3, 0, 2, 4]In [6]: col = [1, 4, 2, 3, 3]In [7]: data = [2, 5, 9, 1, 6]In [8]: coo = sparse.coo_matrix((data, (row, col)), shape=(6, 7))In [9]: print(coo) # coordinate-value format
# (1, 1) 2
# (3, 4) 5
# (0, 2) 9
# (2, 3) 1
# (4, 3) 6In [10]: coo.todense() # coo.toarray() for ndarray instead
Out[10]:
matrix([[0, 0, 9, 0, 0, 0, 0],[0, 2, 0, 0, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 0, 5, 0, 0],[0, 0, 0, 6, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0]])
随着矩阵大小的增加,节省的内存消耗是相当可观的。在稀疏结构中管理数据的成本是固定的,这与密集矩阵的情况不同。由于需要管理子阵列而产生的开销可以忽略不计,大数据场景下,这种稀疏结构使其成为某些数据集的一个很好的选择。
In [11]: def memory_usage(coo):...: # data memory and overhead memory...: coo_mem = (sum(obj.nbytes for obj in [coo.data, coo.row, coo.col])...: + sum(obj.__sizeof__() for obj in [coo, coo.data, coo.row, coo.col]))...: print(f'Sparse: {coo_mem}')...: mtrx = coo.todense()...: mtrx_mem = mtx.nbytes + mtrx.__sizeof__()...: print(f'Dense: {mtrx_mem}')In [12]: memory_usage(coo)
# Sparse: 480
# Dense: 448In [13]: coo.resize(100, 100)In [14]: memory_usage(coo)
# Sparse: 480
# Dense: 80112
键值字典矩阵(Dictionary of Keys Matrix)
Dictionary Of Keys (DOK)与 COO 非常相似,只是它的子类 dict 将坐标数据信息存储为键-值对。由于它使用哈希表作为存储,因此在任何给定位置标识值都需要固定的查找时间。如果需要内置字典的功能,可以使用这种格式,但是要注意,哈希表比数组占用更多的内存。
In [15]: dok = sparse.dok_matrix((10, 10))In [16]: dok[(3, 7)] = 42 # store value 42 at coordinate (3, 7)In [17]: dok[(9, 5)] # zero elements are accessible
Out[17]: 0.0In [18]: dok.keys() | dok.transpose().keys() # union of key views
Out[18]: {(3, 7), (7, 3)}In [19]: isinstance(dok, dict)
Out[19]: True
注意: 使用从
dict
继承的方法时要小心潜在的问题; 它们并不总是表现良好。
# 例如In [20]: out_of_bounds = (999, 999) # 定义一个元组,表示矩阵中一个超出范围的索引In [21]: dok[out_of_bounds] = 1 # 此行代码按预期会引发IndexError,因为out_of_bounds是一个超出范围的索引IndexError: Index out of bounds. # 正常执行了In [22]: dok.setdefault(out_of_bounds) # 该行代码被静默地忽略了,没有抛出异常...In [23]: dok.toarray() # ...直到这里。该行代码才引发ValueError异常,因为矩阵的行索引超出了矩阵维度的范围。ValueError: row index exceeds matrix dimensions # dok.toarray()返回的异常In [24]: dok.pop(out_of_bounds) # 通过删除超出范围的点来解决问题In [25]: sparse.dok_matrix.fromkeys([..., ..., ...]) # 一整个无语了就,作者这里原文用了一个don't let me started,就是我都抱怨烦了这个问题。这行代码存在问题,它会提醒我缺少一个必需的参数'arg1'TypeError: __init__() missing 1 required positional argument: 'arg1' # 缺少一个必需的参数'arg1'的报错
链表矩阵(Linked List Matrix)
插入数据的最灵活的格式是使用LInked List (LIL)链表矩阵。可以通过 NumPy 的索引和切片语法来设置数据,从而快速填充矩阵。在作者看来,LIL 是从头构造稀疏矩阵的最酷的稀疏格式。
LIL 将信息存储在 LIL.rows
中,其中每个列表表示一个行索引,列表中的元素匹配列。在并行数组lil.data
中,存储 NNZ 值。但是与其他稀疏格式不同的是,这些子数组不能显式地传递给构造函数; LIL 矩阵必须从空状态或从现有的密集或稀疏矩阵中生成。下面是用于构建 LIL 矩阵的各种技术的示例。
In [26]: lil = sparse.lil_matrix((6, 5), dtype=int) # 创建一个6x5的稀疏矩阵,元素数据类型为int,格式是lil,In [27]: lil[(0, -1)] = -1 # 给一个单独的点复制In [28]: lil[3, (0, 4)] = [-2] * 2 # 设置两个点In [29]: lil.setdiag(8, k=0) # 设置主对角线上的元素为8In [30]: lil[:, 2] = np.arange(lil.shape[0]).reshape(-1, 1) + 1 # 设置整个第3列In [31]: lil.toarray() # 将稀疏矩阵转换为密集矩阵输出
Out[31]:
array([[ 8, 0, 1, 0, -1],[ 0, 8, 2, 0, 0],[ 0, 0, 3, 0, 0],[-2, 0, 4, 8, -2],[ 0, 0, 5, 0, 8],[ 0, 0, 6, 0, 0]])
那么缺点是什么?它利用了底层的 (jagged arrays)锯齿数组(又译:不规则数组),这需要 np.dtype (object)
。这比矩形数(rectangular array)组消耗更多的内存,所以如果数据足够大,您可能会被迫使用 COO 而不是 LIL。简而言之,IL 矩阵虽然存在一些缺点,但仍然是一个非常棒的选择!
In [32]: lil.rows # 查看 LIL 矩阵的行索引Out[32]:
array([list([0, 2, 4]), list([1, 2]), list([2]), list([0, 2, 3, 4]),list([2, 4]), list([2])], dtype=object)In [33]: lil.data[:, np.newaxis] # 展示jagged锯齿结构/不规则结构Out[33]:
array([[list([8, 1, -1])],[list([8, 2])],[list([3])],[list([-2, 4, 8, -2])],[list([5, 8])],[list([6])]], dtype=object)
顺便说一句,链表矩阵LIL是一个用词不当,因为它不使用链表幕后!LIL 实际上使用了 Python 的 List,它是一个动态数组,所以不管文档中怎么说,它实际上应该被称为 List Matrix List。(错过了给它命名为 LOL 的机会…)
In [34]: sparse.lil.__doc__ # 读取lili模块的文档Out[34]: 'LInked List sparse matrix class\n' # 我是一个链表稀疏矩阵唷(其实才不是嘞!)
中断
CSR,CSC的介绍见python中稀疏矩阵的常用表示COO LIL CSR CSC【下篇】
python中稀疏矩阵的常用表示COO LIL CSR CSC【上篇】相关推荐
- (numpy)python中Array的常用函数
python中Array的常用函数 1.unique 2.sum 3.max 1.unique a = np.random.randint(10, size=20).reshape(4,5) a &g ...
- Python中random模块常用函数/方法(2)——random.random(),random.randint()和random.uniform()
1.random.random():生成一个0到1的随机符点数: 0 <= n < 1.0 语法:random.random() #生成一个0~1之间的随机浮点数 print(" ...
- python中字典的常用操作命令及注意事项
目录 1. 使用update()合并字典 2. 使用del删除具有指定键的元素 3. 使用clear清除所有元素 4. 使用in判断是否存在 5. 使用[key]获取元素 6. 使用keys()获取所 ...
- python中socket模块常用吗_python中socket模块详解
socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...
- Python中集合的常用操作
一.集合的介绍 1.定义:集合是无序的,集合中的元素是唯一的,集合一般用于元组或者列表中的元素去重. 2.特性:集合的目的是将不同的值存放在一起,不同的集合间用来做关系运算,无须纠结于集合中的单个值. ...
- python1000个常用代码-介绍Python中几个常用的类方法
内置方法 说明 __init__(self,...) 初始化对象,在创建新对象时调用 __del__(self) 释放对象,在对象被删除之前调用 __new__(cls,*args,**kwd) 实例 ...
- python中字典的常用函数_python中得字典和常用函数总结
字典是python中一种常见得数据类型,用{}表示,并且以键值对得形式存放数据. dic={},其中得key键值是不可变得,类型可以是字符串.其中,列表,字典不可以作为键,键值是不可变得.字符串,元组 ...
- python中定义函数常用关键字_Python 中定义函数的关键字是 _________________ 。_学小易找答案...
[其它]实验4-串和数组-实验任务书.docx [填空题]表达式 'abc' in ['abcdefg'] 的值为______________. [填空题]已知 x = range(1,4) 和 y ...
- Python学习总结(10) python中数据的常用操作之切片和迭代
1.切片 (slice ) 符号[ : ] 和Matlab中取任意长的数据方式完全一样! (1) 切片的由来: 取一个list或tuple的部分元素是非常常见的操作.比如,一个list如下: > ...
最新文章
- 强大的 IDEA 代码生成
- LFCS 系列第二讲:如何安装和使用纯文本编辑器 vi/vim
- 英语计算机单词mp3,计算机英语会话(MP3+中英字幕) 第1期:计算机系统(1)
- centos6 rsync+inotify 数据同步
- objective-c 2.0的字面量Literals
- 各种java生成word解决方案的优缺点对比
- Linux文件系统构成
- 并发编程-基础概念介绍
- Jfinal的七牛云存储插件:qiniuPlugin for jfinal.
- USB之基本协议和数据波形1
- android7.1.2安装包,APK.1文件安装器下载-APK.1安装:微信APK安装器下载1.7 安卓版-西西软件下载...
- python3多线程进度条_python,多线程_Python:在多线程中使用进度条(progressbar)碰到的问题,python,多线程 - phpStudy...
- 蓝字冲销是什么意思_会计记账,贷方红字,贷方蓝字什么意思
- 电脑无法进入bios
- 最好听的男孩、女孩名字
- 北科计算机研究生导师推荐,北京科技大学计算机与通信工程学院-【喜报】我院班晓娟老师荣获第三届“研师亦友——我最喜爱的导师”称号...
- 关键字搜索aliexpress商品API接口(速卖通关键词搜索商品接口)
- 初中计算机基础考试试题及答案,计算机基础考试试题及答案(三)
- 推荐多样性重排算法之MMR
- 【ES6闯关】Promise堪比原生的自定义封装then、catch、resolve、reject...