UPDATE: 2022-10-23 11:35:24
tidyr::fill()
を自分で実装してみる、という話。tidyr::fill()
は、前または後ろの要素を使用して、カラムのNA
を埋めてくれる便利な関数。値が繰り返されず、値が変更された場合にのみ記録されるログのようなデータでは本当に便利。
tidyr::fill()
は下記のように機能する。便利な関数である。
library(tidyverse)
<- c(NA,NA,'a',NA,NA,NA,'b','c','d',NA,NA,'e',NA)
x ::tibble(x1 = x, x2 = x, x3 = x) %>%
tibble::fill(x2, .direction = 'down') %>%
tidyr::fill(x3, .direction = 'up') tidyr
## # A tibble: 13 × 3
## x1 x2 x3
## <chr> <chr> <chr>
## 1 <NA> <NA> a
## 2 <NA> <NA> a
## 3 a a a
## 4 <NA> a b
## 5 <NA> a b
## 6 <NA> a b
## 7 b b b
## 8 c c c
## 9 d d d
## 10 <NA> d e
## 11 <NA> d e
## 12 e e e
## 13 <NA> e <NA>
実装方針は下記の画像の通り。まずはNA
を含んでいるベクトルに対して、NA
ではないインデックスを取得する。インデックスの先頭に1
を追加し、NA
ではないインデックス間の差を計算する。この後に、x
のインデックスに対応する要素の値を、NA
ではないインデックス間の差の数分、値をリピートさせる。つまり、元のベクトルのNA
を埋めるのではなく、「NA
を埋めたベクトルを生成するための情報を元のベクトルから取得する」ということになる。入力チェックもろもろはいったんやらない。
<- function(x, updown = TRUE){
na_fill
# if updown arg is FALSE, reverse vaector
if(updown == FALSE){
<- rev(x)
x
}# get not NA index & add first postion
<- which(!is.na(x))
ind <- c(1, ind)
ind
# get repeat count num
<- length(x) + 1
len_x <- diff(c(ind, len_x))
rep_times
# make filluped vector
<- rep(x[ind], times = rep_times)
x
# if updown arg is FALSE, reverse vaector to put it back
if(updown == FALSE){
<- rev(x)
x
}
return(x)
}
これで最低限は実装できているので、動くはず・・・。ということで実行した結果が下記の通りで、期待通りに機能しているように見える。
<- na_fill(x, updown = TRUE)
UpDown <- na_fill(x, updown = FALSE)
DownUp <- na_fill(DownUp, updown = TRUE)
DownUp_UpDown <- na_fill(UpDown, updown = FALSE)
UpDown_DownUp
tibble(
x, UpDown, UpDown_DownUp )
## # A tibble: 13 × 3
## x UpDown UpDown_DownUp
## <chr> <chr> <chr>
## 1 <NA> <NA> a
## 2 <NA> <NA> a
## 3 a a a
## 4 <NA> a a
## 5 <NA> a a
## 6 <NA> a a
## 7 b b b
## 8 c c c
## 9 d d d
## 10 <NA> d d
## 11 <NA> d d
## 12 e e e
## 13 <NA> e e
tibble(
x, DownUp, DownUp_UpDown )
## # A tibble: 13 × 3
## x DownUp DownUp_UpDown
## <chr> <chr> <chr>
## 1 <NA> a a
## 2 <NA> a a
## 3 a a a
## 4 <NA> b b
## 5 <NA> b b
## 6 <NA> b b
## 7 b b b
## 8 c c c
## 9 d d d
## 10 <NA> e e
## 11 <NA> e e
## 12 e e e
## 13 <NA> <NA> e
この発想がすぐに思いつかないし、実装もまだまだなので、まだまだ力が足りていない・・・ことを実感しました。ついでにrep()
も関数の最後に使っているので、やっておく。
<- function(x, repeat_vec){
my_repeat <- 1
index <- vector(mode = typeof(x), length = sum(repeat_vec))
res
for (i in seq_along(repeat_vec)){
for (j in 1:repeat_vec[i]){
<- x[i]
res[index] <- index + 1
index
}
}
return(res)
}
<- c(3,2,1)
rv <- c(1,2,3)
x my_repeat(x = x, repeat_vec = rv)
## [1] 1 1 1 2 2 3
<- c('a','b','c')
s my_repeat(x = s, repeat_vec = rv)
## [1] "a" "a" "a" "b" "b" "c"
<- c(TRUE, FALSE, TRUE)
l my_repeat(x = l, repeat_vec = rv)
## [1] TRUE TRUE TRUE FALSE FALSE TRUE
numpy
は使わない前提でやってみる。つらい…苦しい感がにじみ出ている。
list = [None, None, 'a', None, None, None, 'b', 'c', 'd', None, None, 'e', None]
def fill_na(vector):
= len(vector)
len_vec = [i for i in range(len_vec) if vector[i] is not None]
val_ind 0, 0)
val_ind.insert(
val_ind.append(len_vec)# print('val_ind : {}'.format(val_ind))
# ind : [0, 2, 6, 7, 8, 11, 13]
= len(val_ind)
len_ind = [val_ind[i+1] - val_ind[i] for i in range(len_ind - 1)]
diff_vec # print('diff_vec: {}'.format(diff_vec))
# diff_vec: [2, 4, 1, 1, 3, 2]
= [vector[val_ind[i]] for i in range(len_ind-1)]
base_vec # print('base_vec: {}'.format(base_vec))
# base_vec: [None, 'a', 'b', 'c', 'd', 'e']
= [item for item, repeat_count in zip(base_vec, diff_vec) for i in range(repeat_count)]
result return result
print('Pre : {}'.format(list))
print('Post: {}'.format(fill_na(list)))
# Pre : [None, None, 'a', None, None, None, 'b', 'c', 'd', None, None, 'e', None]
# Post : [None, None, 'a', 'a' , 'a', 'a', 'b', 'c', 'd', 'd', 'd', 'e', 'e']