Mengubah nilai variabel menjadi nama kolom; pengidentifikasi duplikat untuk baris di rapir::spread

Saya sedang mengerjakan file pemilih yang berantakan. Perhatikan tibble berikut ini:

library(dplyr)
library(tidyr)
dat <- tibble(
  id = factor(c("A","B","C","D","E")),
  demographic_info1 = round(rnorm(5),2),
  demographic_info2 = round(rnorm(5),2),
  election_1 = c(NA,"GN2016","GN2016","SE2016","GN2008"),
  election_2 = c(NA,"MT2014","GN2012","GN2016","GN2004"),
  election_3 = c(NA,NA,NA,"MT2014","GN2000"),
  election_4 = c(NA,NA,NA,"GN2012",NA),
  election_5 = c(NA,NA,NA,"MT2010",NA),
)

Yang terlihat seperti:

# A tibble: 5 x 8
      id demographic_info1 demographic_info2 election_1 election_2 election_3 election_4 election_5
  <fctr>             <dbl>             <dbl>      <chr>      <chr>      <chr>      <chr>      <chr>
1      A             -1.50              0.81       <NA>       <NA>       <NA>       <NA>       <NA>
2      B             -1.84             -0.64     GN2016     MT2014       <NA>       <NA>       <NA>
3      C              1.66             -0.10     GN2016     GN2012       <NA>       <NA>       <NA>
4      D              0.91             -0.08     SE2016     GN2016     MT2014     GN2012     MT2010
5      E              0.04             -1.15     GN2008     GN2004     GN2000       <NA>       <NA>
  • Setiap id adalah pengidentifikasi unik untuk seorang pemilih.
  • Dua kolom demographic_info adalah pengisi, hanya untuk menunjukkan bahwa saya ingin mempertahankan nilai-nilai ini ketika saya melakukan pembentukan ulang data.

Kolom election_1 hingga election_5 adalah kolom yang saya minati. Data disusun sedemikian rupa sehingga file tersebut mencakup 5 pemilu terbaru yang pernah diikuti seseorang. election_1 adalah pemilu terbaru, election_5 adalah pemilu terbaru.

Perhatikan bahwa orang A tidak pernah memilih, sedangkan orang D selalu memilih. Yang ingin saya lakukan adalah mengubah kolom ini menjadi sejumlah variabel: SE2016, GN2016, MT2014, GN2012, dll.; yaitu, semua nilai di election_1 hingga election_5. Saya ingin masing-masing variabel ini menjadi TRUE atau FALSE untuk menentukan apakah orang tersebut hadir atau tidak dalam pemungutan suara. Saya telah mencoba kode ini:

dat %>% # take data
  gather(election, race, election_1:election_5) %>% # gather by election
  mutate(temp=TRUE) %>% # make new variable that is all TRUE
  select(-election) %>% # drop election variable
  spread(race, temp, fill=FALSE) # spread by this all TRUE variable, fill all NAs as FALSE

Namun, spread memunculkan kesalahan:

Error: Duplicate identifiers for rows (1, 6, 11, 16, 21), (12, 17, 22), (13, 18, 23), (20, 25)

Hal ini karena terdapat beberapa entri untuk setiap nilai variabel race. Saya sudah mencoba group_by(id) sebelum melakukan spread, tetapi kesalahan yang sama terjadi.

Saya ingin hasil tibble terlihat seperti:

# A tibble: 5 x 11
      id demographic_info1 demographic_info2 SE2016 GN2016 MT2014 GN2012 MT2010 GN2008 GN2004 GN2000
  <fctr>             <dbl>             <dbl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>
1      A             -0.91             -0.56  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE
2      B              1.24             -1.78  FALSE   TRUE   TRUE  FALSE  FALSE  FALSE  FALSE  FALSE
3      C              0.61              0.11  FALSE   TRUE  FALSE   TRUE  FALSE  FALSE  FALSE  FALSE
4      D              2.43             -0.53   TRUE   TRUE   TRUE   TRUE   TRUE  FALSE  FALSE  FALSE
5      E             -1.40             -1.23  FALSE  FALSE  FALSE  FALSE  FALSE   TRUE   TRUE   TRUE

person Mark White    schedule 19.08.2017    source sumber
comment
Anda perlu membuat kolom urutan berdasarkan grup sebelum melakukan spread   -  person akrun    schedule 19.08.2017
comment
@akrun apakah variabel id tidak dihitung sebagai kolom urutan? Tidak yakin saya mengikuti.   -  person Mark White    schedule 19.08.2017


Jawaban (3)


rapir menyediakan beberapa sintaks untuk mengatasi masalah ini.

# set up
library(dplyr)
library(tidyr)
dat <- tibble(
  id = factor(c("A","B","C","D","E")),
  demographic_info1 = round(rnorm(5),2),
  demographic_info2 = round(rnorm(5),2),
  election_1 = c(NA,"GN2016","GN2016","SE2016","GN2008"),
  election_2 = c(NA,"MT2014","GN2012","GN2016","GN2004"),
  election_3 = c(NA,NA,NA,"MT2014","GN2000"),
  election_4 = c(NA,NA,NA,"GN2012",NA),
  election_5 = c(NA,NA,NA,"MT2010",NA)
)

Yang akhirnya kami inginkan adalah TRUE atau FALSE untuk setiap pasangan pemilih (5) x pemilu (8). Saat kami mengumpulkan data ke dalam format panjang, kami hanya melihat kombinasi pemilih x pemilu yang ada dalam kumpulan data.

d_votes <- dat %>%
  gather("variable", "election", election_1:election_5) %>%
  select(-variable) %>%
  mutate(voted = TRUE)
d_votes
#> # A tibble: 25 x 5
#>        id demographic_info1 demographic_info2 election voted
#>    <fctr>             <dbl>             <dbl>    <chr> <lgl>
#>  1      A              0.76             -0.23     <NA>  TRUE
#>  2      B             -0.80              0.08   GN2016  TRUE
#>  3      C             -0.33              1.60   GN2016  TRUE
#>  4      D             -0.50             -1.27   SE2016  TRUE
#>  5      E             -1.03              0.59   GN2008  TRUE
#>  6      A              0.76             -0.23     <NA>  TRUE
#>  7      B             -0.80              0.08   MT2014  TRUE
#>  8      C             -0.33              1.60   GN2012  TRUE
#>  9      D             -0.50             -1.27   GN2016  TRUE
#> 10      E             -1.03              0.59   GN2004  TRUE
#> # ... with 15 more rows

count(d_votes, election)
#> # A tibble: 9 x 2
#>   election     n
#>      <chr> <int>
#> 1   GN2000     1
#> 2   GN2004     1
#> 3   GN2008     1
#> 4   GN2012     2
#> 5   GN2016     3
#> 6   MT2010     1
#> 7   MT2014     2
#> 8   SE2016     1
#> 9     <NA>    13

Kita perlu menghasilkan setiap kombinasi pemilih dan pemilu. fungsi expand() rapir membuat semua kombinasi variabel dari kolom/vektor data yang berbeda. (Ini berfungsi seperti fungsi dasar expand.grid(), jadi nama expand() menggugah).

d_possible_votes <- d_votes %>%
  expand(nesting(id, demographic_info1, demographic_info2),
         election)
d_possible_votes
#> # A tibble: 40 x 4
#>        id demographic_info1 demographic_info2 election
#>    <fctr>             <dbl>             <dbl>    <chr>
#>  1      A              0.76             -0.23   GN2000
#>  2      A              0.76             -0.23   GN2004
#>  3      A              0.76             -0.23   GN2008
#>  4      A              0.76             -0.23   GN2012
#>  5      A              0.76             -0.23   GN2016
#>  6      A              0.76             -0.23   MT2010
#>  7      A              0.76             -0.23   MT2014
#>  8      A              0.76             -0.23   SE2016
#>  9      B             -0.80              0.08   GN2000
#> 10      B             -0.80              0.08   GN2004
#> # ... with 30 more rows

Perhatikan bahwa kita sekarang memiliki 8 pilihan x 5 id = 40 baris.

Kami menggunakan fungsi nesting() untuk memperlakukan setiap set/baris (id, demographic_info1, demographic_info2) sebagai satu unit; demografi bersarang dalam id. Perluasan menyediakan 40 kombinasi (id, demographic_info1, demographic_info2) x election.

Jika kita menggabungkan suara yang diamati ke dalam suara yang mungkin, kolom voted diisi dengan nilai TRUE atau NA. Fungsi replace_na() rapir dapat memperbaiki nilai NA tersebut.

d_possible_votes <- d_possible_votes %>%
  left_join(d_votes) %>%
  replace_na(list(voted = FALSE))
#> Joining, by = c("id", "demographic_info1", "demographic_info2", "election")
d_possible_votes
#> # A tibble: 40 x 5
#>        id demographic_info1 demographic_info2 election voted
#>    <fctr>             <dbl>             <dbl>    <chr> <lgl>
#>  1      A              0.76             -0.23   GN2000 FALSE
#>  2      A              0.76             -0.23   GN2004 FALSE
#>  3      A              0.76             -0.23   GN2008 FALSE
#>  4      A              0.76             -0.23   GN2012 FALSE
#>  5      A              0.76             -0.23   GN2016 FALSE
#>  6      A              0.76             -0.23   MT2010 FALSE
#>  7      A              0.76             -0.23   MT2014 FALSE
#>  8      A              0.76             -0.23   SE2016 FALSE
#>  9      B             -0.80              0.08   GN2000 FALSE
#> 10      B             -0.80              0.08   GN2004 FALSE
#> # ... with 30 more rows

Sekarang, kita dapat menyebarkan pemilu dan mencapai kerangka data yang diinginkan.

spread(d_possible_votes, election, voted)
#> # A tibble: 5 x 11
#>       id demographic_info1 demographic_info2 GN2000 GN2004 GN2008 GN2012 GN2016 MT2010 MT2014 SE2016
#> * <fctr>             <dbl>             <dbl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>
#> 1      A              0.76             -0.23  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE
#> 2      B             -0.80              0.08  FALSE  FALSE  FALSE  FALSE   TRUE  FALSE   TRUE  FALSE
#> 3      C             -0.33              1.60  FALSE  FALSE  FALSE   TRUE   TRUE  FALSE  FALSE  FALSE
#> 4      D             -0.50             -1.27  FALSE  FALSE  FALSE   TRUE   TRUE   TRUE   TRUE   TRUE
#> 5      E             -1.03              0.59   TRUE   TRUE   TRUE  FALSE  FALSE  FALSE  FALSE  FALSE

Pola menghasilkan kombinasi pengidentifikasi, menggabungkan data aktual, dan mengoreksi nilai yang hilang ini sangat umum—sedemikian rupa sehingga rapir menyertakan fungsi complete() untuk melakukan ketiganya sekaligus.

d_votes %>%
  complete(nesting(id, demographic_info1, demographic_info2),
           election, fill = list(voted = FALSE)) %>%
  spread(election, voted)
#> # A tibble: 5 x 11
#>       id demographic_info1 demographic_info2 GN2000 GN2004 GN2008 GN2012 GN2016 MT2010 MT2014 SE2016
#> * <fctr>             <dbl>             <dbl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>
#> 1      A              0.76             -0.23  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE
#> 2      B             -0.80              0.08  FALSE  FALSE  FALSE  FALSE   TRUE  FALSE   TRUE  FALSE
#> 3      C             -0.33              1.60  FALSE  FALSE  FALSE   TRUE   TRUE  FALSE  FALSE  FALSE
#> 4      D             -0.50             -1.27  FALSE  FALSE  FALSE   TRUE   TRUE   TRUE   TRUE   TRUE
#> 5      E             -1.03              0.59   TRUE   TRUE   TRUE  FALSE  FALSE  FALSE  FALSE  FALSE
person TJ Mahr    schedule 21.08.2017
comment
Terima kasih atas penjelasan bagus tentang cara complete bekerja. Saya tidak mengetahui fungsi itu! Apa pendapat Anda tentang solusi saya, jika dibandingkan? Kode saya terasa kurang elegan, tetapi microbenchmark mengatakan kode tersebut berjalan lebih cepat (sekitar 6 milidetik lebih cepat, yang bisa sangat berarti jika saya menjalankannya pada file 1 GB) - person Mark White; 21.08.2017

Kita dapat menggunakan group_by pada 'id' untuk membuat variabel urutan karena 'id' diduplikasi dan kemudian menghapusnya setelah spread

dat %>%
   gather(election, race, election_1:election_5) %>%
   mutate(temp=TRUE)%>% group_by(id) %>%
   mutate(i1 = row_number()) %>% 
   select(-election) %>%
   spread(race, temp, fill=FALSE) %>%
   select(-i1)
person akrun    schedule 19.08.2017
comment
Cemerlang, terima kasih. Hanya untuk memastikan saya memahami mengapa ini berhasil: i1 memberikan pengenal unik dalam setiap grup...? - person Mark White; 19.08.2017
comment
@MarkWhite Ya, karena masing-masing ada 5 baris untuk setiap 'id' yaitu dat %>% gather(election, race, election_1:election_5) %>% mutate(temp=TRUE)%>% count(id) %>% .$n# [1] 5 5 5 5 5 Jadi 'i1' membuat urutan 1, 2, ,3, ..5 untuk setiap 'id' dan dapat membantu dalam penyebaran - person akrun; 19.08.2017
comment
Sekilas, menggunakan ini pada kumpulan data saya yang sebenarnya: Kode di atas membuat lima baris untuk setiap id; satu baris untuk setiap election_* bidang. - person Mark White; 19.08.2017
comment
Saya juga telah melihat tanggapan Anda yang lain, seperti ini (stackoverflow.com/a/43259735/7903456). Sepertinya ini berfungsi untuk tujuan saya, tetapi untuk beberapa alasan saya mendapatkan baris baru untuk setiap bidang election_*. - person Mark White; 20.08.2017

Masalahnya adalah ada entri duplikat untuk nilai NA. Saya memecahkan masalah pengidentifikasi duplikat dan masalah beberapa baris dari jawaban akrun dengan mengambil hanya unique baris, lalu mengelompokkan berdasarkan id:

dat %>%
  gather(election, race, election_1:election_5) %>%
  mutate(temp=TRUE) %>%
  select(-election) %>%
  unique() %>% # GET RID OF DUPLICATE NA ENTRIES
  group_by(id) %>% 
  spread(race, temp, fill=FALSE) %>%
  select(-`<NA>`)

# A tibble: 5 x 11
# Groups:   id [5]
      id demographic_info1 demographic_info2 GN2000 GN2004 GN2008 GN2012 GN2016 MT2010 MT2014 SE2016
* <fctr>             <dbl>             <dbl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>  <lgl>
1      A             -1.19             -0.94  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE  FALSE
2      B              1.41             -0.62  FALSE  FALSE  FALSE  FALSE   TRUE  FALSE   TRUE  FALSE
3      C             -0.21              1.62  FALSE  FALSE  FALSE   TRUE   TRUE  FALSE  FALSE  FALSE
4      D              1.51              0.09  FALSE  FALSE  FALSE   TRUE   TRUE   TRUE   TRUE   TRUE
5      E              0.65             -2.09   TRUE   TRUE   TRUE  FALSE  FALSE  FALSE  FALSE  FALSE
person Mark White    schedule 19.08.2017