在julia中删除单态维数
只是玩玩Julia(1.0),我需要在Python/numpy/matlab中大量使用的一件事是在julia中删除单态维数,julia,Julia,只是玩玩Julia(1.0),我需要在Python/numpy/matlab中大量使用的一件事是squere函数来删除单例维度 我发现在朱莉娅身上做到这一点的一种方法是: a = rand(3, 3, 1); a = dropdims(a, dims = tuple(findall(size(a) .== 1)...)) 第二行看起来有点笨重,不容易立即阅读和解析(这也可能是我从其他语言带来的偏见)。然而,我想知道这是否是Julia中实现这一点的标准方法?你可以去掉元组,改用逗号,: drop
squere
函数来删除单例维度
我发现在朱莉娅身上做到这一点的一种方法是:
a = rand(3, 3, 1);
a = dropdims(a, dims = tuple(findall(size(a) .== 1)...))
第二行看起来有点笨重,不容易立即阅读和解析(这也可能是我从其他语言带来的偏见)。然而,我想知道这是否是Julia中实现这一点的标准方法?你可以去掉元组,改用逗号
,
:
dropdims(a, dims = (findall(size(a) .== 1)...,))
这个问题的实际答案令我惊讶。你所问的可以改为: 为什么
dropdims(a)
不删除所有单例维度
我将引用Tim Holy的话:
不可能让压缩(A)返回编译器需要的类型
可以推断---输入矩阵的大小是一个运行时变量,所以
编译器无法知道输出有多少个维度
我会的。所以它不可能给你想要的类型稳定性
撇开类型稳定性不谈,您所写的还有其他一些令人惊讶的含义。例如,请注意:
julia> f(a) = dropdims(a, dims = tuple(findall(size(a) .== 1)...))
f (generic function with 1 method)
julia> f(rand(1,1,1))
0-dimensional Array{Float64,0}:
0.9939103383167442
总之,在Base
Julia中包含这样的方法会鼓励用户使用它,从而导致潜在的类型不稳定的代码,在某些情况下,这些代码不会很快(核心开发人员正极力避免这一点)。在像Python这样的语言中,严格的类型稳定性是不强制的,因此您会发现这样的函数
当然,没有什么可以阻止您定义自己的方法。我不认为你会找到一个更简单的写作方法。例如,未实施的Base
命题是方法:
function squeeze(A::AbstractArray)
singleton_dims = tuple((d for d in 1:ndims(A) if size(A, d) == 1)...)
return squeeze(A, singleton_dims)
end
请注意使用它的潜在影响。我对科林的启示有点惊讶;当然,依赖于“重塑”的东西是类型稳定的吗?(另外,作为奖励,还可以返回视图而不是副本) 否?让我简单地补充一点,“不受控制的”
dropdims
(删除任何单个维度)是常见的错误源。例如,假设您有一个循环,该循环要求从某个外部源获取一个数据数组a
,然后在该循环上运行R=sum(a,dims=2)
,然后去掉所有单例维度。但是,假设10000次中有一次,您的外部源返回A
,其中size(A,1)
恰好为1:boom,突然您下降的维度超过了您的预期,并且可能存在严重误解数据的风险
如果您手动指定这些维度(例如,
dropdims(R,dims=2)
),那么您就不会受到类似的错误的影响。我想不出比您所做的更简单的事情了。我想这里有一个参数,调用dropdims(a)
应该使用您上面定义的方法,即自动删除所有单例维度。编辑:事实证明,它已经被广泛讨论,请参阅,并感谢这项了不起的研究。我想,由于运行时对大小的依赖,没有办法以有效的方式实现这一点?@Luca是的,这正是问题所在。请注意,在许多用例中,类型稳定性性能的影响很小,可以使用屏障功能等技术来缓解,因此不要完全放弃这样做。你只需要意识到其中的含义。(我相信你现在可以明白为什么他们选择不把它包含在库中了)
)是的,绝对是。我可以理解为什么它不是标准库的一部分,并且很高兴看到这些决策中的讨论@ColinTBowers你的意思是在最后一个片段中说返回dropdims(A,singleton_dims)
?@tasospapstyllanou不,我实际上是直接从我在回答中链接的PR复制了这个片段。PR是从2017年年中开始的,当时是挤压
而不是下降
。我想我本可以把它改成dropdims,但我真正想证明的是,提交的PR与OP提出的方法没有明显不同。我还懒洋洋地复制/粘贴:-)嗯,好的。所以,由于重塑返回一个视图,它基本上可以将其转换为任何推断的数据类型,并且没有惩罚?恐怕我对朱莉娅来说太陌生了,无法对此作出评论或判断。我希望有更多经验的人能加入进来。不,这不是可以推断的,因为数组的大小不是由类型系统编码的;考虑<代码>类型(挤压(RAND(3,5))< /代码> VS代码>类型(挤压(RAND(1,5))< /代码> VS代码>类型(挤压(RAND(1,1))< /代码>。这是不可推断的keepdims
部分,而不是重塑
。感谢@tholy的澄清。从效率的角度来看,这真的那么重要吗?在我看来,这样一个挤压函数将是一个简单的包装器,在引擎盖下调用优化方法。由于没有进一步的说明,我看不出编译版本会提供更多的说明。我错过了这里的大局吗?@Luca这不是因为它是惯用的,而是因为它们代表了不同的事物Tuple
是类型及其相应的构造函数,它需要一个iterable参数进行初始化tuple
是一个独立的函数,它接受一系列参数并构造一个对应的元组,可能是通过在引擎盖下适当地调用tuple
。这曾经是一种常见的模式(例如,Julia 0.3将int64
作为函数转换为int64
,但现在已经不支持直接使用构造函数),但现在已经不多了。事情一直在变化,所以,谁知道呢。@tasospapstyllanou,这取决于:如果你把结果数组传递给一个函数f
,你可能会没事的---朱莉娅将不得不做一次“动态分派”,以
julia> function squeeze( A :: AbstractArray )
keepdims = Tuple(i for i in size(A) if i != 1);
return reshape( A, keepdims );
end;
julia> a = randn(2,1,3,1,4,1,5,1,6,1,7);
julia> size( squeeze(a) )
(2, 3, 4, 5, 6, 7)