2016-04-11 8 views
0

У меня проблема с двумя частями. Я искал весь стек и нашел ответы, связанные с моими проблемами, но никакие варианты, которые я пробовал, еще не работали. Заранее благодарю за любую помощь!Добавление стандартизованных переменных в кадр данных с использованием dplyr и цикла for

У меня есть большой фрейм данных, который содержит много переменных.

Первый, я хочу (1) стандартизировать переменную другой переменной (в моем случае, динамик), и (2) отфильтровать значения после того, как переменная была стандартизирована (более 2 стандартных отклонений от имею в виду). (1) и (2) может быть рассмотрена с помощью функции dplyr.

Second У меня есть много переменных, для которых я хочу это сделать, поэтому я пытаюсь найти автоматизированный способ сделать это, например, с циклом for.

Задача 1: Дать функцию, содержащую функции dplyr

Вот пример того, что мой кадр данных выглядит следующим образом:

df = data.frame(speaker=c("eng1","eng1","eng1","eng1","eng1","eng1","eng2","eng2","eng2","eng2","eng2"), 
      ratio_means001=c(0.56,0.202,0.695,0.436,0.342,10.1,0.257,0.123,0.432,0.496,0.832), 
      ratio_means002=c(0.66,0.203,0.943,0.432,0.345,0.439,0.154,0.234,NA,0.932,0.854)) 

Выход:

 speaker ratio_means001 ratio_means002 
1  eng1   0.560   0.660 
2  eng1   0.202   0.203 
3  eng1   0.695   0.943 
4  eng1   0.436   0.432 
5  eng1   0.342   0.345 
6  eng1   10.100   0.439 
7  eng2   0.257   0.154 
8  eng2   0.123   0.234 
9  eng2   0.432    NA 
10 eng2   0.496   0.932 
11 eng2   0.832   0.854 

Ниже базовый код Я хочу превратиться в функцию:

standardized_data = group_by(df, speaker) %>% 
mutate(zRatio1 = as.numeric(scale(ratio_means001)))%>% 
filter(!abs(zRatio1) > 2) 

Так что кадр данных будет выглядеть следующим образом (к примеру):

 speaker ratio_means001 ratio_means002 zRatio1 
    (fctr)   (dbl)   (dbl)  (dbl) 
1  eng1   0.560   0.660 -0.3792191 
2  eng1   0.202   0.203 -0.4699781 
3  eng1   0.695   0.943 -0.3449943 
4  eng1   0.436   0.432 -0.4106552 
5  eng1   0.342   0.345 -0.4344858 
6  eng2   0.257   0.154 -0.6349445 
7  eng2   0.123   0.234 -1.1325034 
8  eng2   0.432    NA 0.0148525 
9  eng2   0.496   0.932 0.2524926 
10 eng2   0.832   0.854 1.5001028 

Вот что я имею в терминах функции до сих пор. Частичные работы мутировать, но я боролся с добавлением фильтра:

library(lazyeval) 
standardize_variable = function(col1, new_col_name) { 
    mutate_call = lazyeval::interp(b = interp(~ scale(a)), a = as.name(col1)) 
    group_by(data,speaker) %>% 
    mutate_(.dots = setNames(list(mutate_call), new_col_name)) %>% 
    filter_(interp(~ !abs(b) > 2.5, b = as.name(new_col_name))) # this part does not work 
} 

я получаю следующее сообщение об ошибке при попытке запустить функцию:

data = standardize_variable("ratio_means001","zRatio1") 

Error in substitute_(`_obj`[[2]], values) : 
argument "_obj" is missing, with no default 

Задача 2: Циклическое над функция

Есть много переменных, к которым я бы хотел применить вышеприведенную функцию, поэтому я хотел бы найти способ использовать цикл или другую полезную функцию, чтобы помочь автоматизировать этот процесс. Имена переменных отличаются только числом в конце, так что я придумал что-то вроде этого:

d <- data.frame() 
for(i in 1:2) 
{ 
col1 <- paste("ratio_means00", i, sep = "") 
new_col <- paste("zRatio", i, sep = "") 
d <- rbind(d, standardize_variable(col1, new_col)) 
} 

Однако, я получаю следующее сообщение об ошибке:

Error in match.names(clabs, names(xi)) : 
names do not match previous names 

Еще раз спасибо за любую помощь на эти проблемы!

+0

добавьте небольшой пример ваших данных – rawr

+1

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

+0

Пример данных. Спасибо за отзыв Имо. Я обсуждал снятие второй части с проблемы, но две части взаимосвязаны для моей конкретной проблемы, поэтому я решил оставить вторую часть. Может быть более простой способ решить проблему в целом, где два шага могут быть объединены в один (но я еще не понял этого). Еще раз спасибо за ваши ответы! – Amanda

ответ

0

Альтернатива 1

Я считаю, что главная проблема, которую вы имели с функцией должны были сделать с вами вызова interp дважды.Фиксация, которая привела к дополнительной проблеме с filter, которая, я думаю, была вызвана scale, добавляя атрибуты (я использую версию разработки dplyr, dplyr_0.4.3.9001). Обертывание as.numeric вокруг scale избавляется от этого.

Так с исправлениями ваша функция выглядит следующим образом:

standardize_variable = function(col1, new_col_name) { 
    mutate_call = lazyeval::interp(~as.numeric(scale(a)), a = as.name(col1)) 
    group_by(df, speaker) %>% 
     mutate_(.dots = setNames(list(mutate_call), new_col_name)) %>% 
     filter_(interp(~ !abs(b) > 2, b = as.name(new_col_name))) 
} 

Я нашел петлю через переменные, чтобы быть немного более сложным, чем то, что вы были, как я полагаю, вы хотите объединить свои наборы данных вместе один раз вы делаете один для каждой переменной. Один из вариантов - сохранить их в списке, а затем использовать do.call с merge, чтобы получить окончательный набор данных.

d = list() 
for(i in 1:2) { 
    col1 <- paste("ratio_means00", i, sep = "") 
    new_col <- paste("zRatio", i, sep = "") 
    d[[i]] = standardize_variable(col1, new_col) 
} 

do.call(merge, d) 

    speaker ratio_means001 ratio_means002 zRatio1 zRatio2 
1 eng1   0.202   0.203 -0.4699781 -1.1490444 
2 eng1   0.342   0.345 -0.4344858 -0.6063693 
3 eng1   0.436   0.432 -0.4106552 -0.2738853 
4 eng1   0.560   0.660 -0.3792191 0.5974521 
5 eng1   0.695   0.943 -0.3449943 1.6789806 
6 eng2   0.123   0.234 -1.1325034 -0.7620572 
7 eng2   0.257   0.154 -0.6349445 -0.9590348 
8 eng2   0.496   0.932 0.2524926 0.9565726 
9 eng2   0.832   0.854 1.5001028 0.7645194 

Вариант 2

В качестве альтернативы все это было бы использовать mutate_each и rename_ для первой части задачи, а затем использовать interp с lapply петли для окончательной фильтрации всех масштабированных переменных одновременно.

В приведенном ниже коде я воспользовался тем фактом, что mutate_each позволяет назначать имена для одиночных функций, начиная с dplyr_0.4.3.9001. Все выглядит немного сложнее в rename_, потому что я делал имена, которые вы хотели для новых столбцов. Чтобы упростить вещи, вы можете оставить их в _z от mutate_each и сэкономить сложный шаг rename_ с gsub и grepl.

df2 = df %>% 
    group_by(speaker) %>% 
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>% 
    rename_(.dots = setNames(names(.)[grepl("z", names(.))], 
         paste0("zR", gsub("r|_z|_means00", "", names(.)[grepl("z", names(.))])))) 

Как только это будет сделано, вам просто нужно будет фильтровать несколько столбцов. Я думаю, что проще всего составить список условий, которые вы хотите отфильтровать, используя interp и lapply, а затем дать это аргументу .dotsfilter_.

dots = lapply(names(df2)[starts_with("z", vars = names(df2))], 
         function(y) interp(~abs(x) < 2, x = as.name(y))) 

filter_(df2, .dots = dots) 

Source: local data frame [9 x 5] 
Groups: speaker [2] 

    speaker ratio_means001 ratio_means002 zRatio1 zRatio2 
    (fctr)   (dbl)   (dbl)  (dbl)  (dbl) 
1 eng1   0.560   0.660 -0.3792191 0.5974521 
2 eng1   0.202   0.203 -0.4699781 -1.1490444 
3 eng1   0.695   0.943 -0.3449943 1.6789806 
4 eng1   0.436   0.432 -0.4106552 -0.2738853 
5 eng1   0.342   0.345 -0.4344858 -0.6063693 
6 eng2   0.257   0.154 -0.6349445 -0.9590348 
7 eng2   0.123   0.234 -1.1325034 -0.7620572 
8 eng2   0.496   0.932 0.2524926 0.9565726 
9 eng2   0.832   0.854 1.5001028 0.7645194 

Альтернатива 3

Я часто нахожу эти проблемы наиболее просто, если я изменить набор данных вместо того, чтобы работать по столбцам. Например, все еще используя самую новую версию mutate_each, но пропустив шаг переименования для простоты, вы можете gather все стандартизованные столбцы, используя функцию gather от tidyr, а затем filter новый столбец.

library(tidyr) 

df %>% 
    group_by(speaker) %>% 
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>% 
    gather(group, zval, ends_with("_z")) %>% 
    filter(abs(zval) <2) 

# First 12 lines of output 

Source: local data frame [20 x 5] 
Groups: speaker [2] 

    speaker ratio_means001 ratio_means002   group  zval 
    <fctr>   <dbl>   <dbl>   <chr>  <dbl> 
1  eng1   0.560   0.660 ratio_means001_z -0.3792191 
2  eng1   0.202   0.203 ratio_means001_z -0.4699781 
3  eng1   0.695   0.943 ratio_means001_z -0.3449943 
4  eng1   0.436   0.432 ratio_means001_z -0.4106552 
5  eng1   0.342   0.345 ratio_means001_z -0.4344858 
6  eng2   0.257   0.154 ratio_means001_z -0.6349445 
7  eng2   0.123   0.234 ratio_means001_z -1.1325034 
8  eng2   0.432    NA ratio_means001_z 0.0148525 
9  eng2   0.496   0.932 ratio_means001_z 0.2524926 
10 eng2   0.832   0.854 ratio_means001_z 1.5001028 
11 eng1   0.560   0.660 ratio_means002_z 0.5974521 
12 eng1   0.202   0.203 ratio_means002_z -1.1490444 
... 

Если желаемая конечная форма широкий формат, вы можете использовать spread (также из tidyr для этого. Одно из преимуществ (для меня) в том, что вы можете хранить все значения одной переменной, даже если другая переменная провалили этап фильтрации.

df %>% 
    group_by(speaker) %>% 
    mutate_each(funs(z = as.numeric(scale(.))), starts_with("ratio_means00")) %>% 
    gather(group, zval, ends_with("_z")) %>% 
    filter(abs(zval) <2) %>% 
    spread(group, zval) 

Source: local data frame [11 x 5] 
Groups: speaker [2] 

    speaker ratio_means001 ratio_means002 ratio_means001_z ratio_means002_z 
    <fctr>   <dbl>   <dbl>   <dbl>   <dbl> 
1  eng1   0.202   0.203  -0.4699781  -1.1490444 
2  eng1   0.342   0.345  -0.4344858  -0.6063693 
3  eng1   0.436   0.432  -0.4106552  -0.2738853 
4  eng1   0.560   0.660  -0.3792191  0.5974521 
5  eng1   0.695   0.943  -0.3449943  1.6789806 
6  eng1   10.100   0.439    NA  -0.2471337 
7  eng2   0.123   0.234  -1.1325034  -0.7620572 
8  eng2   0.257   0.154  -0.6349445  -0.9590348 
9  eng2   0.432    NA  0.0148525    NA 
10 eng2   0.496   0.932  0.2524926  0.9565726 
11 eng2   0.832   0.854  1.5001028  0.7645194 

Если вы не хотите, чтобы сохранить NA, вы всегда можете na.omit их на более позднее время.

+0

Большое спасибо за ваш полезный ответ, аосмит! Для Alternative 1 все отлично работало с образцами данных, но функция слияния не работала с моими реальными данными, когда у меня было больше двух переменных, но я нашел способ обойти эту [ссылку] (http: //stackoverflow.com/questions/8091303/simultaneously-merge-multiple-data-frames-in-a-list). Для второй альтернативы я не смог воспроизвести ваши результаты. Я застрял на df2 с этой ошибкой: Ошибка в gsub («r | _z | _means00», «», names (.) [Ends_with («z», vars = names (.))]): не удалось найти function "ends_with" – Amanda

+0

@Amanda Я использую версию разработки * dplyr *, см. [здесь] (https://github.com/hadley/dplyr). Это позволяет указывать имена в 'mutate_each' и использовать вспомогательные функции' select'. – aosmith

+0

А теперь я понимаю. Спасибо за оба изящных решений! – Amanda