用先前的非缺失值填充“缺失”值的有效方法是什么?

时间:2019-11-06 02:20:12

标签: julia

我有一个向量

using Missings
v = allowmissing(rand(100))
v[rand(100) .< 0.1] .= missing

用最后一个非缺失值填充v的最佳方法是什么?

当前

for (i, val) in enumerate(v)
  ismissing(val) && (i >=2) && (v[i]=v[i-1])
end
first_non_missing = findfirst(x->!ismissing(x), v)
if first_non_missing >= 2
  v[1:first_non_missing -1] .= v[first_non_missing]
end
v = disallowmissing(v)

但是我发现对于大向量它的速度很慢。用先前的非缺失值填充缺失值的一种优雅而有效的方法是什么?

3 个答案:

答案 0 :(得分:2)

一个简单而快速的解决方案:

replace_missing!(v) = accumulate!((n0,n1) -> ismissing(n1) ? n0 : n1, v, v, init=zero(eltype(v)))

答案 1 :(得分:1)

您需要一个init值,以防丢失第一个值,而我无法执行您的代码。但话虽如此,这是我的尝试:

$ ffprobe data\test\00001\00001.mp4
ffprobe version 4.2.1 Copyright (c) 2007-2019 the FFmpeg developers
  built with gcc 9.1.1 (GCC) 20190807
  configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'data\test\00001\00001.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.29.100
  Duration: 00:02:52.18, start: 0.000000, bitrate: 1643 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 1512 kb/s, 24 fps, 24 tbr, 90k tbn, 48 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

答案 2 :(得分:1)

以下答案完全基于此线程中的讨论:Julia DataFrame Fill NA with LOCF。更具体地说,它基于丹麦 Shrestha、Dan Getzbtsays 的回答。

正如 laborg 暗示的那样,Base Julia 中的 accumulate 函数将完成这项工作。

假设我们有一个数组:a = [1, missing, 2, missing, 9]。我们想用 missing 替换第一个 1,用 2 替换第二个:a = [1, 1, 2, 2, 9],即 a = a[[1, 1, 3, 3, 5]] ([ 1, 1, 3, 3, 5] 这里是索引)。

这个函数将完成这项工作:

ffill(v) = v[accumulate(max, [i*!ismissing(v[i]) for i in 1:length(v)], init=1)]

顺便说一句,“ffill”的意思是“向前填充”,这是我从 Pandas 中采用的名称。

我会在下面解释。

accumulate 函数的作用是根据我们输入的数组返回一个新数组。

对于像我这样刚接触 Julia 的人:在 Julia 的数学运算中,i*true = ii*false=0。因此,当数组中的元素不丢失时,则 i*!ismissing() = i;否则,i*!ismissing() = 0

在 a = [1, missing, 2, missing, 9] 的情况下,[i*!ismissing(a[i]) for i in 1:length(a)] 将返回 [1, 0, 3, 0, 5]。由于此数组位于 accumulate 函数中,其中操作为 max,因此我们将得到 [1, 1, 3, 3, 5]

然后 a[[1, 1, 3, 3, 5]] 将返回 [1, 1, 2, 2, 9]

这就是为什么

a = ffill(a)

将得到 [1, 1, 2, 2, 9]

现在,您可能想知道为什么我们在 init = 1 中有 ffill(v)。说,b = [missing, 1, missing, 3]。然后,[i*!ismissing(b[i]) for i in 1:length(b)] 将返回 [0, 2, 0, 4]。然后 accumulate 函数将返回 [0, 2, 2, 4]。下一步,b[[0, 2, 2, 4]] 将抛出错误,因为在 Julia 中,索引从 1 开始而不是 0。因此,b[0] 没有任何意义。

init = 1 函数中使用 accumulate,我们将得到 [1, 2, 2, 4] 而不是 [0, 2, 2, 4] 因为 1({{1} } 我们设置)大于 0(第一个数字)。

我们可以在这里更进一步。上面的 init 函数仅适用于单个数组。但是如果我们有一个大型数据框呢?

比如说,我们有:

ffill()
using DataFrames

a = ["Tom", "Mike", "John", "Jason", "Bob"]
b = [missing, 2, 3, missing, 8]
c = [1, 3, missing, 99, missing]
df = DataFrame(:Name => a, :Var1 => b, :Var2 => c)

在这里,Dan Getz's answer 派上用场:

julia> df

5×3 DataFrame
 Row │ Name    Var1     Var2    
     │ String  Int64?   Int64?  
─────┼──────────────────────────
   1 │ Tom     missing        1
   2 │ Mike          2        3
   3 │ John          3  missing 
   4 │ Jason   missing       99
   5 │ Bob           8  missing 
nona_df = DataFrame([ffill(df[!, c]) for c in names(df)], names(df))