[翻译] NumSharp的数组切片功能 [:]

原文地址:https://medium.com/scisharp/slicing-in-numsharp-e56c46826630

翻译初稿(英文水平有限,请多包涵):

由于Numsharp新推出了数组切片这个牛逼的功能,所以.NET社区距离拥有强大的开源机器学习平台又近了一步。

Python之所以是机器学习的首选语言,部分原因就是因为它拥有一些牛逼的库,例如NumPyTensorflow。与此同时,C#开发人员也迫切需要用于机器学习和数据科学的强大开源库。而NumSharp,这个由 SciSharp STACK这个开源组织全力推动的,要把NumPy移植到C#的这个项目,由于其最近全面实现了切片技术,从而向该目标迈进了一大步。该技术允许对n维数组随意的创建子集,并将其作为对原始数据的高效视图。因为这些,使得它与TensorFlow.net一起成为了C#中机器学习的有用工具。

到底有啥大不了的?

如果你没用过NumPy,你可能不知道切片技术有多好用, Python数组允许通过对一定范围对元素进行索引来返回数组的一个切片,其索引操作是这样的:a[start:end:step]。但是,只有使用NumPy复杂巧妙的数组实现,切片才成为一种真正强大的数据操作技术,若没有这种技术,机器学习或数据科学就无法想象了。

对于那些不能或不想因为机器学习就转换到Python语言的人来说,幸运的事情发生了,我对此也很羡慕, NumSharp将这种能力带入了.NET世界里。作为NumSharp的开发人员之一,我将向您展示几个重要的切片用例,并附有C#的示例代码段。首先请注意,由于语言语法的不同,在C#中无法以与Python相同的方式进行索引。但是,我们决定保留Python里切片定义的语法,因此在C#里,我们使用字符串来索引切片。

而使用NumSharp写出的C#代码也是差不多一样的。但请注意,这里有一个细微的差别是,这里的切片使用的是字符串作为索引器的参数进行的索引。

正如您所看到的,NumSharp团队花了很多的精力来保证代码尽可能的与Python相似。 这非常重要,因为这样的话,现有的依赖于NumPy的代码就可以很轻松的移植到C#上去了。

用例: 使用同一数据的多个视图

对于运行时性能,尤其是对于大规模的数据集而言,能够在不进行复制的情况下仅对函数传入和传出底层数据的本地部分(即大镜像像的一小块)是至关重要的。切片使用局部坐标进行索引的,因此您的算法无需了解数据的全局结构,这样就有效地简化了您的工作,并确保尽可能高的性能,因为避免了不必要的复制。

用例:稀疏视图和递归切片

除了对切片的范围指定start和end之外,再通过指定它的step(步幅),就可以创建数组的稀疏视图了。这是一个连C# 8.0新的数组切片语法都没有的功能(据我所知)。在使用交错数据时,此功能变得非常重要。您可以通过设计算法来让它们处理连的续数据并为它们提供模拟连续数据源的稀疏切片,从而尽可能降低算法的复杂性。

切片可以进一步切片,如果您使用高维数据的话,这也将是一个非常重要的功能。同时这也有助于减少算法的复杂性,因为通过递归切片减少了数据的维数。

用例:高效地处理高维数据

如果您需要将数据数组视为一个卷,并在不需要进行令人烦躁的坐标转换计算的情况下使用其中的某些部分,那么.reshape()方法就是您的朋友。

所有由.reshape()或切片操作创建的数组都只是原始数据的视图。当您对视图的元素进行迭代、读取或写入时,其实您访问的是原始的数据数组。很显然,NumSharp为您做了相应的索引变换,所以您可以使用相对的坐标对切片进行索引。

用例:在无任何额外成本的情况下颠倒元素的顺序

使用值为负数的step(步幅)可以高效的反转切片的顺序。它的优点是不需要复制或列举数据就可以完成此操作,就像IEnumerable.Reverse() 一样。区别在于,视图(就是指a["::-1"]的操作结果)以相反的顺序显示数据,此外您无需对其进行列举就可以索引到该反转序列。

用例:通过减少维度来降低复杂性

当处理高维数据时,该数据的算法也会变得非常复杂。在处理NumSharp的NDArray的.ToString() 方法时(这个方法可以打印出任意高维卷)我注意到该算法通过系统地和递归地将(N-1)D卷切出ND-卷等诸如此类的方式简单而优雅的取得了结果。

通过在可返回低维子卷的范围符号上使用NumSharp的索引符号进行切片,才使这种分而治之的方法变得可行。

范围符号 vs 索引符号

范围符号[“start:stop:step”]允许您访问具有相同维度给定卷的子范围。所以即使只划出二维矩阵的一列,仍然可以得到只有一列的二维矩阵。下面这一小段C#代码就展示了这一点:

索引符号为您提供了一个在N维父卷指定位置切出来的(N-1)维切片。因此,用索引符号从二维矩阵中分割出一个列,可以得到一个一维向量:

如果您一眼也没有发现差异,那么下面这两个切片定义, ange [":,2:3"] vs index [":,2"],它们的结果是大不相同的。NumSharp wiki提供了新切片表示法的完整参考。

附注:ArraySlice <T>

在实现N维视图的切片时,我得出这样一个结论,对于.NET中的许多其他领域来说它可能很有趣,因此我将它分解出一个自己的独立库SliceAndDice。它里面有个东西叫做ArraySlice <T>,它是对所有索引的C#数据结构(如T[]或IList<T>)的一个轻量级包装,此外它还允许您使用相同的塑形,切片和视图机制,并且无需进行任何其他的重度数值计算。它只使用了几百行代码就漂亮简洁的完成了切片的壮举。

综上

NumSharp最近被赋予了同切片和视图机制,同样就是这些机制让NumPy成为Python机器学习生态最重要的库之一。SciSharp Stack作为一个开源组织,目前只有少数技术娴熟的开发人员,但他们却非常努力地要为.NET世界带来同样的魔力。NumSharp最近的这次改进就是实现这一目标的重要基石。

原文地址:https://www.cnblogs.com/cgzl/p/10925023.html

时间: 2024-10-25 21:39:58

[翻译] NumSharp的数组切片功能 [:]的相关文章

窥探Swift之数组安全索引与数组切片

在Swift中的数组和字典中下标是非常常见的,数组可以通过索引下标进行元素的查询,字典可以通过键下标来获取相应的值.在使用数组时,一个常见的致命错误就是数组越界.如果在你的应用程序中数组越界了,那么对不起,如果由着程序的性子的话是会崩溃的.为了防止崩溃呢,我们会对集合做一些安全的处理.比如对数组进行扩展,从而对数组的索引进行安全检查,保证数组的index在正常范围内.在Objective-C中也是经常对数组,字典等做一些处理操作. 今天的博客的主要内容是先对Objective-C中常用集合的安全

golang中的数组切片

概念:基于数组,数组切片添加了一系列管理功能,可以随时动态扩充存放空间,并且不会导致所管理的元素被重复复制. 创建数组切片: 方法一,基于数组: package main import "fmt" func main() { //define an array var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} //create a slice based on array var mySlice []int = m

Swift编程语言翻译与学习——数组

Swift 语言提供经典的数组和字典两种集合类型来存储集合数据.数组用来按顺序存储相同类型的数据.字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对). Swift 语言里的数组和字典中存储的数据值类型必须明确. 这意味着我们不能把不正确的数据类型插入其中. 同时这也说明我们完全可以对获取出的值类型非常自信. Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误. 注意: Swift 的数组结构在被

GO中的数组切片

GO中的数组切片可以看做是功能更强大的数组,在append数据时,可以自动调整内存大小以适应数据实际大小,有些类似于C#中的List<T>. GO 中数组切片的“容量”与实际储存的大小可以不同,比如一个杯子容量是500ml,实际只装了200ml的水. 数组切片的基本用法: fmt.Println("数组切片可以看做是一种比普通数组更强大的数组")     //数组切片     fmt.Println("数组切片:")     fmt.Println(&q

Python进阶:自定义对象实现切片功能

Python进阶:自定义对象实现切片功能 切片是 Python 中最迷人最强大最 Amazing 的语言特性(几乎没有之一),在<Python进阶:切片的误区与高级用法>中,我介绍了切片的基础用法.高级用法以及一些使用误区.这些内容都是基于原生的序列类型(如字符串.列表.元组......),那么,我们是否可以定义自己的序列类型并让它支持切片语法呢?更进一步,我们是否可以自定义其它对象(如字典)并让它支持切片呢? 1.魔术方法:__getitem__() 想要使自定义对象支持切片语法并不难,只需

Go语言中的数组与数组切片

Go中的数组与C的数组一样,只是定义方法不同 c: int a[10][10] Go [10][10]int 定义并初始化 array1 := [5]int{1,2,3,4,5} 变量名 := [index]类型{也可不加} 1.元素访问:1.跟C语言一样通过数组来访问 2.通过关键字range来访问 如:for i,v :=range array{ fmt.Printlb(*Array element[",i,"]=*,v) }第一个表示下标从0到len(array)-1 2.值类型

go语言-golang基础-数据类型数组,数组切片,映射

7.7 数组 数组是Go语言编程中最常用的数据结构之一.顾名思义,数组就是指一系列同一类型数据的集合.数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度. 以下为一些常规的数组声明方法: [32]byte // 长度为32的数组,每个元素为一个字节 [2*N] struct { x, y int32 } // 复杂类型数组 [1000]*float64 // 指针数组 [3][5]int // 二维数组 [2][2][2]float64 // 等同于[2

数组切片算法

题目如下:就是一个数组,切三刀,从这三个切口组成的四个切片的整数和是否能够四等分,如果可以,就返回true,不行就返回false. 主要的思路就是记着三个切点为m1,m2,m3(和是不包括m1 m2 和m3的),则先找m1,m2,头尾的和相等后,记为count,利用count以及m1,m3来查找m2.其中的细节在于,第一次找到的count不一定是对的,这样需要扩大count 继续寻找.具体代码如下. 代码实现如下 1 package p2; 2 3 public class ali { 4 5

golang中不定参数与数组切片的区别

package main import "fmt" func main() { myfunc1(88, 42, 12, 56) //传递不定数量的参数 myfunc2([]int{88, 42, 12, 56}) //传递一个数组切片 } func myfunc1(args ...int) { //接受不定数量的参数,这些参数的类型全部是int for _, arg := range args { fmt.Println(arg) } } func myfunc2(args []int