在julia中删除单态维数

在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

只是玩玩Julia(1.0),我需要在Python/numpy/matlab中大量使用的一件事是
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)