在Julia中删除连续重复项

8

我很新手 Julia,不知道如何删除数组中连续的重复元素。例如,如果你有这个数组:

   `v=[8,8,8,9,5,5,8,8,1];`

我想获得向量v1,使其满足以下条件:

v1 = [8,9,5,8,1];

有人能帮忙吗?非常感谢。


是的,你说得对,它应该包括1。抱歉! - Samy Jelassi
1
@SamyJelassi 你可以编辑你的问题,包括 1 - niczky12
5个回答

8

一个方法是定义:

function fastuniq(v)
  v1 = Vector{eltype(v)}()
  if length(v)>0
    laste = v[1]
    push!(v1,laste)
    for e in v
      if e != laste
        laste = e
        push!(v1,laste)
      end
    end
  end
  return v1
end

通过这个函数,您将获得以下内容:

julia> println(fastuniq(v))
[8,9,5,8,1]

但是,在处理数组时,需要决定元素是要进行深拷贝还是浅拷贝。对于整数而言,这并不重要。


这是最快的方法吗? - Samy Jelassi
1
非常快。也许一些优化指令,如在for循环中使用@inbounds,可以帮助提高性能。push!函数已经进行了优化,以便许多小的push!操作不会频繁地进行内存分配。 - Dan Getz
1
如果删除的元素百分比通常很小,您可能还想要有一个sizehint!来优化输出数组的分配。另一件加速的事情(如果经常找不到重复项)是拥有一个快速扫描第一个重复项的函数,如果没有找到重复项,则简单地返回原始数组,或者使用copy(取决于是否需要副本)。 - Scott Jones

7
StatsBase.jl 中有一个名为rle的函数(Run-length encoding),可以实现这个功能。

2
不错的解决方案。它还揭示了 rle 中一个潜在的 bug:一个空向量会导致异常(在访问第一个元素之前没有检查)。 - Dan Getz
之前的评论导致了这个 PR:https://github.com/JuliaStats/StatsBase.jl/pull/154 。本质上,当 v 为空向量时,不变量 inverse_rle(rle(v)...) == v 仍应该起作用。 - Dan Getz

5
这比@DanGetz的函数慢得多,但以下是一种只需一行即可完成的方法:
function notsofastunique(v)
  return [v[1]; v[2:end][v[2:end] .!= v[1:end-1]]]
end


>println(notsofastunique(v))
[8,9,5,8,1]

也许对于寻求矢量化解决方案的人会有所帮助。

lag(a, n=1) = append!(fill(NaN, n), a[1:(end-n)]); v[v .!= lag(v)]同样的意思,但可能更清晰。 - ivo Welch

4

为了达到 @niczky12 的简洁解决方案的目标,以下代码使用了 Iterators.jl 包(非常有用且正在逐步迁移到 Base 库中)。

using Iterators           # install with Pkg.add("Iterators")

neatuniq(v) = map(first,filter(p->p[1]!=p[2],partition(chain(v,[nothing]),2,1)))

虽然没有进行过任何基准测试,但它应该没问题(但比较长的for循环函数要慢一些)。


2

为了练习而已...

这里有另一个小函数可用于执行此操作,该函数仅适用于非负值(包括0)。

function anotherone(v)
    v1 = zeros(eltype(v),length(v))
    v1[1]=v[1]+1
    for e = 2:length(v)
        if v[e] != v[e-1]
            v1[e] = v[e]+1    
        end
    end
    return v1[find(v1)]-1
end

编辑:

根据评论中的输入,添加了另一个版本。我认为这个版本应该更快,也许你可以测试一下 :) 这个版本也适用于负数。

function anotherone(v)
    v1 = falses(length(v))
    v1[1]=true
    for e = 2:length(v)
        if v[e] != v[e-1]
            v1[e] = true    
        end
    end
    return v[v1]
end

1
一个更简单的方法是直接使用逻辑索引。将v1设置为逻辑数组(v1 = falses(length(v))),然后在分配元素的位置将其设置为true。现在你可以简单地返回v[v1],它将适用于所有值和元素类型。 - mbauman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接