Как переставить фрейм данных с переменными / наблюдением в строке и столбце?(используя dplyr и tidyr) - PullRequest
0 голосов
/ 08 декабря 2018

В R как справиться с грязным фреймом данных со смешанной строкой и столбцом в качестве переменных?

days <- c(as.Date("2011-07-01") + 0:9)
set.seed(10)
d <- data.frame(days,replicate(9,round(runif(10,0,10),3)))
names(d) <- c("Date",  "x.astreet.1", "x.astreet.2", "x.astreet.3",
                       "x.Bstreet.1", "x.Bstreet.2", "x.Bstreet.3",
                       "x.Cstreet.1", "x.Cstreet.2", "x.Cstreet.3")
streetnames <- c(NA,rep(c("Astr.","Bstr.","Cstr."),3))
molecule <- c(NA, rep(c("SO","CO","O3"),3))

d <- rbind(streetnames, molecule,d)

см. Df как tbl на этом экране печати

в этом случае в идеале должно быть только 5 строк (Дата, SO, NO, O3, Station)

Ответы [ 4 ]

0 голосов
/ 08 декабря 2018

Вот мой подход.Преимущество такого способа состоит в том, что он полностью программный.Хорошо иметь решение, в котором вы вручную переименовываете переменные, если набор данных завершен, но этот подход может масштабироваться до набора данных, если вы все еще добавляете новые станции и газы.

# OP changed the 'streetnames' vector, below is the correct one they've provided.

days <- c(as.Date("2011-07-01") + 0:9) 
set.seed(10) 
d <- data.frame(days,replicate(9,round(runif(10,0,10),3))) 
names(d) <- c("Date", "x.astreet.1", "x.astreet.2", "x.astreet.3", 
              "x.Bstreet.1", "x.Bstreet.2", "x.Bstreet.3", 
              "x.Cstreet.1", "x.Cstreet.2", "x.Cstreet.3") 
streetnames <- c(NA,rep(c("Astr."),3),rep(c("Bstr."),3),rep(c("Cstr."),3))
molecule <- c(NA, rep(c("SO","CO","O3"),3))  

d <- rbind(streetnames, molecule, d)

# ---------------

library(tidyr)
library(dplyr)
library(janitor)

# Replace column names with the combined first two rows. This is tricky to do inside
# a dplyr pipeline so I do it outside.
names(d) <- paste(d[1,], d[2,])

d2 <- 
    d %>% 
    slice(3:n()) %>%                     # Remove first 2 rows
    clean_names() %>%                    # Janitor standardises column names
    rename(date = na_na) %>%
    gather(measure, value, -date) %>%    # Collapse wide to long
    separate(measure,                    # Break this column into several columns
             into = c("station", "gas")) %>%
    mutate_at("value", as.numeric) %>% 
    # You can stop there to have a long table. To get a wide table:
    spread(gas, value) %>% 
    identity()

head(d2)

#>         date station    co    o3    so
#> 1 2011-07-01    astr 6.517 8.647 5.075
#> 2 2011-07-01    bstr 2.755 3.543 5.356
#> 3 2011-07-01    cstr 0.756 8.614 0.319
#> 4 2011-07-02    astr 5.677 6.154 3.068
#> 5 2011-07-02    bstr 2.289 9.364 0.931
#> 6 2011-07-02    cstr 5.344 4.644 1.145

str(d2)

#> 'data.frame':    30 obs. of  5 variables:
#>  $ date   : Date, format: "2011-07-01" "2011-07-01" "2011-07-01" ...
#>  $ station: chr  "astr" "bstr" "cstr" "astr" ...
#>  $ co     : num  6.517 2.755 0.756 5.677 2.289 ...
#>  $ o3     : num  8.65 3.54 8.61 6.15 9.36 ...
#>  $ so     : num  5.075 5.356 0.319 3.068 0.931 ...

Примечание: я всегдабросить identity() в конце конвейера для целей отладки.Это позволяет вам закомментировать целые строки канала, не беспокоясь о том, что следует за %>% ошибками повышения.

0 голосов
/ 08 декабря 2018

Я думаю, это то, к чему ты пытаешься добраться.Вероятно, существует более элегантное решение, но оно будет работать.

Я предположил, что суффиксы 1, 2, 3 соответствуют SO, CO и O3.

Это решение не используетназвания улиц или векторы molucule_number, которые вы создали, так что вы можете оставить вызов rbind(), который вы сделали.

library(dplyr)
library(tidyr)

e <- d %>% gather(key = "station", value = "val", x.astreet.1:x.Cstreet.3)

SO <- e %>% filter(grepl("1", station)) 
CO <- e %>% filter(grepl("2", station)) 
O3 <- e %>% filter(grepl("3", station))

f <- data.frame(SO, CO %>% select(val), O3 %>% select(val))

g <- f %>% mutate(Station = case_when(station == "x.astreet.1" ~ "Astr",
                                      station == "x.Bstreet.1" ~ "Bstr",
                                      station == "x.Cstreet.1" ~ "Cstr"),
                  SO = val,
                  CO = val.1,
                  O3 = val.2) %>%
           select(Date, SO, CO, O3, Station)

Я оставил в DF переименование, чтобы вы могли видеть результат после каждого шага.

0 голосов
/ 08 декабря 2018

Начиная с начала вашего примера кода с пропущенными вызовами rbind:

days <- c(as.Date("2011-07-01") + 0:9)
set.seed(10)
d <- data.frame(days,replicate(9,round(runif(10,0,10),3)))
names(d) <- c("Date",  "x.astreet.1", "x.astreet.2", "x.astreet.3",
                       "x.Bstreet.1", "x.Bstreet.2", "x.Bstreet.3",
                       "x.Cstreet.1", "x.Cstreet.2", "x.Cstreet.3")

d %<>% gather(col_name, value, -Date) %>%
    separate(col_name, c("x", "street_name", "molecule_number"), sep = "\\.", convert = TRUE) %>%
    select(-x) %>%
    spread(molecule_number, value) %>%
    rename(SO = `1`, NO = `2`, O3 = `3`)
0 голосов
/ 08 декабря 2018

Базовый подход R может быть следующим.

res <- lapply(seq(2, ncol(d), by = 3), function(i){
  Date <- d[-(1:2), "Date"]
  SO <- d[-(1:2), i]
  CO <- d[-(1:2), i + 1]
  O3 <- d[-(1:2), i + 2]
  data.frame(Date, SO, CO, O3)
})
res <- do.call(rbind, res)
res$Date <- as.Date(res$Date)
row.names(res) <- NULL

head(res)
#        Date    SO    CO    O3
#1 2011-07-01 5.075 6.517 8.647
#2 2011-07-02 3.068 5.677 6.154
#3 2011-07-03 4.269 1.135 7.751
#4 2011-07-04 6.931 5.959 3.556
#5 2011-07-05 0.851  3.58 4.058
#6 2011-07-06 2.254 4.288 7.066
...