Сопоставить строки по всем столбцам в кадре данных и вменять или заменять значения - PullRequest
0 голосов
/ 09 мая 2019

Задача

У меня есть фрейм данных, df, с 82 столбцами, где после четвертого столбца остальные столбцы содержат одинаковую начальную строку в трех экземплярах. Например. mass.mean, mass.stdev, mass.rsd, density.mean, density.stdev, density.rsd и т. Д. Мне нужно:

1) Сопоставьте три столбца с частичными строками (например, mass или density) и

2) Заменить значения этих сопоставленных столбцов конкретными вычислениями, если выполняются условия (например, если столбцы в df содержат строку mass, то замените NA на предыдущее значение (вменяемое) zoo::na.locf или если столбцы в df содержат строку density, затем заменяют NA на ноль.

Мне кажется, мне нужно было бы включить grepl lapply и ifelse лестницу, но я не могу собрать ее вместе. Если бы я мог избежать преобразования из широкого в длинный, это было бы лучше, так как мой фрейм данных содержит> 450 тыс. Строк.

Пример кадра данных

set.seed(123)
df <- data.frame("A" = sample(0:100,8), 
             "B" = sample(0:100,8),
             "C" = sample(0:100,8), 
             "D" = sample(0:100,8),
             "mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1), 
             "mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
             "mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1), 
             "denisty.mean" = c(6, 5, 7, NA, NA, NA, 6, 4), 
             "denisty.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
             "denisty.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7),
             stringsAsFactors = FALSE)

print(df)

   A  B  C  D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66         1          1      0.0            6             3         0.8
2 78 45  4 70        NA         NA       NA            5             1         0.2
3 40 94 32 53         2          1      0.1            7             1         2.0
4 86 44 93 58         3          1      0.1           NA            NA          NA
5 91 65 86 28        NA         NA       NA           NA            NA          NA
6  4 54 66 14        NA         NA       NA           NA            NA          NA
7 50  9 60 91         2          2      0.2            6             2         0.5
8 83 84 97 84         1          1      0.1            4             1         0.7

Желаемый выход

   A  B  C  D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66         1          1      0.0            6             3         0.8
2 78 45  4 70         1          1      0.0            5             1         0.2
3 40 94 32 53         2          1      0.1            7             1         2.0
4 86 44 93 58         3          1      0.1            0             0         0.0
5 91 65 86 28         3          1      0.1            0             0         0.0
6  4 54 66 14         3          1      0.1            0             0         0.0
7 50  9 60 91         2          2      0.2            6             2         0.5
8 83 84 97 84         1          1      0.1            4             1         0.7

Ответы [ 2 ]

1 голос
/ 10 мая 2019

Определите is.mass, чтобы определить столбцы mass, а затем запустите na.locf для них. (Вторая строка na.locf выполняет обратное заполнение в случае, если есть ведущие NA. Вы можете опустить эту строку, если знаете, что их нет, или если вы хотите сохранить ведущие NA.) Аналогичным образом определите is.density, чтобы указать столбцы плотности. и затем используйте na.fill на тех. Альтернативой двум na.locf строкам является одиночная строка df[is.mass] <- na.approx(df[is.mass], method = "constant", rule = 2)

library(zoo)

df.orig <- df # optional in case you want to keep the input around

is.mass <- grepl("mass", names(df))
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE)
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE, fromLast = TRUE)

is.density <- grepl("density", names(df))
df[is.density] <- na.fill(df[is.density], 0)

дает:

> df

   A  B  C  D mass.mean mass.stdev mass.rsd density.mean density.stdev density.rsd
1 29 55 24 66         1          1      0.0            6             3         0.8
2 78 45  4 70         1          1      0.0            5             1         0.2
3 40 94 32 53         2          1      0.1            7             1         2.0
4 86 44 93 58         3          1      0.1            0             0         0.0
5 91 65 86 28         3          1      0.1            0             0         0.0
6  4 54 66 14         3          1      0.1            0             0         0.0
7 50  9 60 91         2          2      0.2            6             2         0.5
8 83 84 97 84         1          1      0.1            4             1         0.7

Примечание

Мы использовали это в качестве ввода. Это то же самое, что и в вопросе, за исключением того, что мы исправили орфографическую ошибку в плотности. Также мы исключили stringsAsFactors, поскольку данные полностью числовые.

set.seed(123)
df <- data.frame("A" = sample(0:100,8), 
             "B" = sample(0:100,8),
             "C" = sample(0:100,8), 
             "D" = sample(0:100,8),
             "mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1), 
             "mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
             "mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1), 
             "density.mean" = c(6, 5, 7, NA, NA, NA, 6, 4), 
             "density.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
             "density.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7))
0 голосов
/ 09 мая 2019

Что-то вроде этого должно решить вашу проблему с колонками плотности:

library(dplyr)
df %>% 
    mutate_at(vars(starts_with("density")),function(x) {if_else(is.na(x),0,x)})

Массовое значение немного сложнее, так как вам нужно получить предыдущие значения, и похоже, что вы хотите рассчитать, когда в последний раз было значение не-NA. это решение обрабатывает случай, когда первая строка содержит NA, сохраняя NA, так как я не уверен, что вы хотите, чтобы произошло.

imputePrev <- function(x) {
    l <- seq_along(x) # declare vector of appropriate length rather than growing
    for (i in seq_along(x)){
        if (i == 1){
            l[i] <- x[i] # always keep the first row
            next
        } else if (is.na(x[i])){
            for (j in 1:(i-1)) { # get the last non-NA value if one is available
                if (!is.na(x[i-j])){
                    l[i] <- x[i-j]
                    break
                }
            }
        } else {
            l[i] <- x[i]
        }
    }
    return(l)
}

df %>%  mutate_at(vars(starts_with("mass")),imputePrev)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...