R和Python中的数组

密集数据连续存储在内存中,由单个索引(内存地址)。阵列内存排序方案解释了这一点将单个索引转换为与数组对应的多个索引协调。例如,矩阵有两个索引:行和列。三维数组有三个,依此类推。

列主要顺序

Column-major柱Fortran、Matlab、R和大多数底层核心线性代数库(BLAS)。顺序地址位置转换为数组坐标i、 j、k…所以第一个数组坐标随地址变化最快下一个数组的坐标速度较慢,以此类推地址位置1、2、3、4被转换为二乘二矩阵坐标系(i,j)作为:

地址i j1       1  12       2  13       1  24       2  2

这个短语专栏作家来自矩阵示例,其中顺序寻址的数据沿以下列顺序排列矩阵。

“行”和“列”的概念并不直接适用于n-d数组,但同样的观点也成立。例如,R语言按顺序布局地址从1、2、…、8转换为2x2x2三维数组,如下所示:

地址ijk1       1  1  12       2  1  13       1  2  14       2  2  15       1  1  26       2  1  27       1  2  28       2  2  2

考虑上面显示的三维情况。给定数组维数d日1=2,d2=2,d=2,一个公式采用基于1的坐标i、 j、k并返回地址位置
a=i+(j-1)*d1+(k)-1) *天2*d日1.

Row-mahor顺序

Row-mahor排序是序列地址索引和数组坐标,而不是在矩阵中跨行的内存。Row-mahor排序有时称为“C”样式排序和列主排序“Fortran”样式。Python/NumPy将数组标志中的顺序引用为C_CONTIGUOUS和分别为F_CONTIGUOUS。例如地址位置1、2、3、4转换为2x2矩阵坐标系(i,j)作为:

地址i j1       1  12       1  23       2  14       2  2

对于已订购的row-mahor,存在BLAS例程的高效包装器数组。为了完整起见,这里有一个2x2x2三维布局示例:

地址ijk1       1  1  12       1  1  23       1  2  14       1  2  25       2  1  16       2  1  27       2  2  18       2  2  2

与上面类似,本例中的一个公式将这些地址索引的基于1的数组坐标为:
a=k+(j-1) *天+(i-1)*d*d日2.

有关行和的通用公式,请参见以下注释列顺序坐标到地址映射,但请注意基于零的索引。

蟒蛇

Python NumPy库非常通用。它可以使用任何一个row-mahor或column-major有序数组,但默认为row-machorNumPy还支持复杂的意见数据的自定义跨越非相邻的内存区域。

显示阵列

R显示具有明确标注的坐标索引的阵列数据。Python没有显示这一点,而是以不同的顺序显示n-d数组数据而不是R(这让R用户感到有些困惑)。考虑一下下面的示例在R中创建并显示相同的4x3x2阵列和Python:

阵列(1:24,c(c)(4,,2))

## , 1
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
##
## , 2
##      [,1] [,2] [,3]
## [1,]   13   17   21
## [2,]   14   18   22
## [3,]   15   19   23
## [4,]   16   20   24
进口numpy公司作为净现值
np.重塑(np.范围(1,25), (4,,2),“F”)

##阵列([[[1,
##         [ 5, 17],
##         [ 9, 21]],
##
##        [[ 2, 14],
##         [ 6, 18],
##         [10, 22]],
##
##        [[ 3, 15],
##         [ 7, 19],
##         [11, 23]],
##
##        [[ 4, 16],
##         [ 8, 20],
##         [12, 24]]])

更容易知道哪个坐标在R中的位置,因为它们是标记。Python使用column-major排序显示相同的内容但以不同的顺序将第一个指数分组在一起订单。要看到这些数组实际上是,相同的,让我们沿着第一个“行”选择值,即带有1(R)或0(Python)的第一个索引:

阵列(1:24,c(c)(4,,2))[1,,删除=错误的]

## , 1
##      [,1] [,2] [,3]
## [1,]    1    5    9
##
## , 2
##      [,1] [,2] [,3]
## [1,]   13   17   21
np.重塑(np.范围(1,25), (4,,2),“F”)[0]

##数组([1,13],
##        [ 5, 17],
##        [ 9, 21]])

我指定了Rdrop=假保留数组的参数维度。如果我们使用下降=真(默认设置)然后R以列主顺序返回一个3x2数组–结果完全相同如上面的Python。

阵列(1:24,c(c)(4,,2))[1,,删除=真的]

##      [,1] [,2]
## [1,]    1   13
## [2,]    5   17
## [3,]    9   21

注意,Python结果是原始数组的特殊视图数据,而不是副本。在这种情况下,它不会存储在连续内存中地址,并不是真正的行或列主。如所示数组标志:

np.重塑(np.范围(1,25), (4,,2),“F”)[0].标志

##C_CONTIGUOUS:错误
##F_CONTIGUOUS:错误
##OWNDATA:错误
##可写:正确
##对齐:正确
##UPDATEIFCOPY:错误

小心网状

网状包让我们可以轻松地混合R和Python代码和数据。回想一下,R表示所有按列主顺序排列的密集数组,但Python/NumPy可以更普遍地表示密集数组。那个差异值得注意,并且很容易导致混淆!

使用R和Python数组时,请记住以下几点,尤其地n个-d个数组n>2.

  1. 密集R数组以column-major-NumPy的形式呈现给Python/NumPy数组。
  2. 全部NumPy数组(列主、行主,否则)是以column-major数组的形式呈现给R,因为这是唯一一种R理解的密集阵列。
  3. R和Python打印数组的方式不同。

同样值得了解的是:

第3点。引入了最潜在的混淆。让我们看看在一些例子中探讨这些问题。

下面的示例使用本机在Python中创建一个2x2x2数组NumPy-row-mahor排序并将其导入R。尽管它们的打印方式不同,实际上是一样的。

图书馆(网状)
净现值<- 进口(“numpy”,转换=错误的)
(x)<-净现值$阿兰奇(1,9)$重塑(2L(左),2L(左),2L(左)))

## [[[ 1.  2.]
##   [ 3.  4.]]
##
##  [[ 5.  6.]
##   [ 7.  8.]]]

(年)<- py_to_r(x) )

## , 1
##      [,1] [,2]
## [1,]    1    3
## [2,]    5    7
##
## , 2
##      [,1] [,2]
## [1,]    2    4
## [2,]    6    8

等一下!他们看起来不一样!但请记住Python的打印顺序是不同的。第一个“行”(第一个索引值)被分组一起。让我们取出R结果中第一个索引为1的元素,删除和不删除未使用的维度以精确显示我们在这里进行索引:

年[1,,,删除=错误的]

## , 1
##      [,1] [,2]
## [1,]    1    3
##
## , 2
##      [,1] [,2]
## [1,]    2    4

年[1,,,删除=真的]

##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

请注意,这与Python中打印的第一个块相同以上输出!这些数组在Python和R中实际上是相同的,分别是。他们之间的明显差异仅仅是印刷。

另一个例子

让我们用另一个例子再次看一下,这次用沿每个维度使用不同长度的数组以使事物均匀更清晰(希望如此)。考虑构造以下4x3x2阵列在Python中,按row-mahor顺序:

净现值<- 进口(“numpy”,转换=错误的)
(x)<-净现值$重塑(np$阿兰奇(1,25),c(c)(4L(左),L(左),2L(左))))

## [[[  1.   2.]
##   [  3.   4.]
##   [  5.   6.]]
##
##  [[  7.   8.]
##   [  9.  10.]
##   [ 11.  12.]]
##
##  [[ 13.  14.]
##   [ 15.  16.]
##   [ 17.  18.]]
##
##  [[ 19.  20.]
##   [ 21.  22.]
##   [ 23.  24.]]]

(年)<- py_to_r(x) )

## , 1
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    7    9   11
## [3,]   13   15   17
## [4,]   19   21   23
##
## , 2
##      [,1] [,2] [,3]
## [1,]    2    4    6
## [2,]    8   10   12
## [3,]   14   16   18
## [4,]   20   22   24

同样,它们看起来很不同,但R和Python数组是真的是一样的。让我们选择第三个索引为0的子数组(Python),相当于R中的第三个索引=1。

净现值$(x,0L(左),2L(左))

## [[  1.   3.   5.]
##  [  7.   9.  11.]
##  [ 13.  15.  17.]
##  [ 19.  21.  23.]]

y[,1]

##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    7    9   11
## [3,]   13   15   17
## [4,]   19   21   23

数字Pytake()函数在这个示例中是等价的到Python符号x[:,:,0]; 即,条目三维索引=0。请参见https://numpy.org/doc/stable/reference/generated/numpy.take.html了解更多信息。

相应的R符号,年[,1],返回相同的这个例子中的东西是:一个4x3矩阵。

尽管内部内存顺序不同,尤其是打印阵列的尴尬差异,阵列是相同的在每种语言中分别以相同的方式编制索引。

从R列主数组到Python怎么样?

前面的示例主要关注row-mahor数组用Python构建。让我们看看开始时会发生什么column-major数组,并在Python中使用它们。

(年)<- 阵列(1:24,c(c)(4,,2)))#在R中

## , 1
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
##
## , 2
##      [,1] [,2] [,3]
## [1,]   13   17   21
## [2,]   14   18   22
## [3,]   15   19   23
## [4,]   16   20   24

(x)<-净现值$阵列(y) )#现在用Python

## [[[ 1 13]
##   [ 5 17]
##   [ 9 21]]
##
##  [[ 2 14]
##   [ 6 18]
##   [10 22]]
##
##  [[ 3 15]
##   [ 7 19]
##   [11 23]]
##
##  [[ 4 16]
##   [ 8 20]
##   [12 24]]]

注意,Python版本利用了NumPy的非凡之处灵活性并保留R的列主顺序:

x个$旗帜

##C_CONTIGUOUS:错误
##F_CONTIGUOUS:正确
##OWNDATA:正确
##可写:正确
##对齐:正确
##UPDATEIFCOPY:错误

您可能已经从前面的部分中了解到数组是相同的,并且遵循相同的索引约定。下一个示例选择一个子数组,使每个数组的第三个索引为0(Python)或1(R):

y[,1]

##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

净现值$(x,0L(左),2L(左))

## [[ 1  5  9]
##  [ 2  6 10]
##  [ 3  7 11]
##  [ 4  8 12]]

重要的是要记住,Python保留了顺序将数组结果复制回R时:

py_to_r(np$(x,0L(左),2L(左)))

##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

结果是,由R或Python创建的数组两种语言的索引完全相同。

但我在R中创建的数组最终与我创建的数组相比被转置了用Python创建?

正确的。这只是默认列主的一个简单结果R和NumPy中分别使用的和row-mahor格式。你总是例如,可以在Python中直接使用R的column-major格式使用下面的“F”标志(对于Fortran):

净现值$重塑(np$阿兰奇(1,25),c(c)(4L(左),L(左),2L(左)),“F”)

## [[[  1.  13.]
##   [  5.  17.]
##   [  9.  21.]]
##
##  [[  2.  14.]
##   [  6.  18.]
##   [ 10.  22.]]
##
##  [[  3.  15.]
##   [  7.  19.]
##   [ 11.  23.]]
##
##  [[  4.  16.]
##   [  8.  20.]
##   [ 12.  24.]]]

注意,结果与我们从R开始得到的结果一样以上。

将R数组重新排列为行主顺序需要更多的工作不如Python灵活,我们不能显式更改R的内存订单表示。当数组是矩阵时,我们可以简单地使用byrow=真。在n-d数组情况下,问题的一部分可以简化为使用byrow=真其次是明智指数置换开孔()。这里有一个低效示例:

<- 阿佩尔姆(阵列(矩阵(1:24,c(c)( * 4,2),旁观者=真的),
           c(c)(,4,2)),c(c)(2,1,))

另一个例子请参见下面的最后一节。

我们可以验证上述难看的表达式是否确实复制了NumPy row-major数组,从本机Python中减去R数组一个:

净现值<- 进口(“numpy”,转换=错误的)
o个<- 进口(“操作员”,转换=错误的)

o个$附属的(np$阿兰奇(1,25)$重塑(4L(左),L(左),2L(左)),np$阵列(y) )

## [[[ 0.  0.]
##   [ 0.  0.]
##   [ 0.  0.]]
##
##  [[ 0.  0.]
##   [ 0.  0.]
##   [ 0.  0.]]
##
##  [[ 0.  0.]
##   [ 0.  0.]
##   [ 0.  0.]]
##
##  [[ 0.  0.]
##   [ 0.  0.]
##   [ 0.  0.]]]

上面的NumPy数组是相同的,它们在元素上的差异是零。

重塑阵列

在R中,通常使用尺寸<-()功能。例如:

昏暗的(x)<- c(c)(1000,28,28)

在R中,此操作只是更改数组的“dim”属性,使用以下命令有效地重新解释指定的数组索引column-major语义。

然而,NumPy重塑方法使用row-mahor默认情况下是语义的,因此如果您混合使用R和Python代码重塑阵列,您会发现如果你使用R尺寸<-()功能。

为了克服这种不一致性数组_重新显示()将重塑R阵列的函数使用row-major语义(即将在row-mahor而非col-mahor命令)。上面的例子是改写为:

x个<- 阵列_重塑(x,c(c)(1000,28,28))

下面是一个进一步的示例来说明差异:

#让我们从4个元素的向量构造一个2x2数组
x个<- 1:4

#重排将沿行填充数组
阵列_重塑(x,c(c)(2,2))
#      [,1] [,2]
# [1,]    1    2
# [2,]    3    4

#设置维度沿列“填充”数组
昏暗的(x)<- c(c)(2,2)
x个
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4

其他差异需要谨慎

值得注意的是,R的类似物应用()功能在Python中的行为不同。以下是优秀的马西萨鲁斯参考https://mathesaurus.sourceforge.net/r-numpy.html应用很好地处理矩阵和向量,但会误导n个-d阵列具有n>2.

马修修斯特别指出,如果是一个矩阵,则可以计算Python中每个列的总和通过a.sum(0)和R(以及其他可能的方式)应用(a,2,sum)。虽然对于矩阵来说是正确的,但这是在将军说得不太对。NumPy的更精确R模拟a.sum(0)应用(a,seq_along(dim(a))[-1],总和)换句话说,a.sum(0)表示第一个维度上的总和,返回与相同维度的数组但具有第一维度远离的。

很容易被混淆,所以让我们看一个使用4x3x2阵列,Python中的第一个:

图书馆(网状)
净现值<- 进口(“numpy”,转换=错误的)
x个<-净现值$阿兰奇(1,25)$重塑(c(c)(4L(左),L(左),2L(左)))
x个$总和(0)#注意:3x2矩阵!

## [[ 40.  44.]
##  [ 48.  52.]
##  [ 56.  60.]]

#注意:这里需要一个tuple()对象(NumPy向量无效)
x个$总和(元组(1L(左),2L(左)))

## [  21.   57.   93.  129.]

现在R中相应的总和:

<- py_to_r(x)
应用(年),昏暗的(年)[-1],总和)

##      [,1] [,2] [,3]
## [1,]   40   48   56
## [2,]   44   52   60

应用(年),1,总和)

## [1]  21  57  93 129

解决出现的问题

这些注释是针对张量流问题编写的网状包装https://github.com/rstudio/reticulate/issues/9. The这个问题直接导致了R中的n-d数组的常见混淆源和Python,以及它们是如何打印和存储的。经过轻微编辑复制了问题中的参考Python代码如下所示。

图书馆(张量流)
净现值<- 进口(“numpy”,转换=错误的)
<-净现值$阿兰奇(1,9)$重塑(c(c)(2L(左),2L(左),2L(左)))
b条<-净现值$阿兰奇(1,5)$重塑(c(c)(2L(左),2L(左),1L(左)))
c(c)<-tf公司$马特姆(tf$常数(a) 、tf$常数(b) )
tf公司$会话()$运行(c)

## , 1
##      [,1] [,2]
## [1,]    5   11
## [2,]   39   53

该问题继续使用R生成的数组重现示例如下:

A类<- 列表(矩阵(1:4,nrow公司=2,旁观者=T) ,矩阵(5:8,nrow公司=2,旁观者=T) )
A类<- 阵列(未列出的(A) ,暗淡=c(c)(2,2,2))

然而,此时我们已经看到R生成的数组A类与上述数组不同通过比较具有np$数组(A)如下所示。

然而,我们可以看到他们是如何容易犯错误的因为数组的打印方式相同!R数组外观与打印的Python数组相同。

打印(a)

## [[[ 1.  2.]
##   [ 3.  4.]]
##
##  [[ 5.  6.]
##   [ 7.  8.]]]


打印(名词)$阵列(A) )

## [[[1 5]
##   [2 6]]
##
##  [[3 7]
##   [4 8]]]


打印(A)

## , 1
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4
##
## , 2
##      [,1] [,2]
## [1,]    5    6
## [2,]    7    8

相反,我们需要构造R数组A类不同于匹配Python的row-mahor顺序,如前几节所述。我们可以使用多种方法,包括:

(A)<-净现值$阵列(阿佩尔姆(阵列(1:8,c(c)(2,2,2)),c(c)(,2,1))))

## [[[1 2]
##   [3 4]]
##
##  [[5 6]
##   [7 8]]]

同样小心地对中的值进行排序b条阵列,我们可以完成在R中复制示例(结果与参考上面的Python示例)。

A类<-净现值$阵列(阿佩尔姆(阵列(1:8,c(c)(2,2,2)),c(c)(,2,1)))
B类<-净现值$阵列(阿佩尔姆(阵列(1:4,c(c)(2,2,1)),c(c)(2,1,)))
C类<-tf公司$马特姆(tf$常数(A) 、tf$常数(B) )
tf公司$会话()$运行(C)

## , 1
##      [,1] [,2]
## [1,]    5   11
## [2,]   39   53