Удалите выпадающие строки по столбцу и коэффициент в R - PullRequest
1 голос
/ 04 мая 2020

Я работаю с фреймом данных в R. У меня есть следующая функция, которая удаляет все строки фрейма данных df, где для указанного индекса / атрибута столбца значение в этой строке находится за пределами среднего ( столбца) плюс или минус n * stdev (столбца).

remove_outliers <- function(df,attr,n){
  outliersgone <- df[df[,attr]<=(mean(df[,attr],na.rm=TRUE)+n*sd(df[,attr],na.rm=TRUE)) & df[,attr]>=(mean(df[,attr],na.rm=TRUE)-n*sd(df[,attr],na.rm=TRUE)),]
  return(outliersgone)
}

Мой вопрос состоит из двух частей.

(1) В моем фрейме данных df также есть столбец «Группа», в котором указывается метка класса. Я хотел бы иметь возможность удалять выбросы в соответствии со средним значением и стандартным отклонением в своей группе в столбце, то есть организованными по факторам (в столбце). Таким образом, вы должны удалить из фрейма данных строку, помеченную группой A, если в указанном столбце / атрибуте значение в этой строке находится за пределами среднего (для строк группы A в этом столбце) плюс / минус n * stdev (для группы Строки в этом столбце). И то же самое для групп B, C, D, E, F, et c.

Как я могу это сделать? (Желательно использовать только базовый R и dplyr.) Я попытался использовать df %>% group_by(Group), за которым следует mutate, но я не уверен, что передать для изменения, учитывая, что моя функция remove_outliers, похоже, требует, чтобы весь фрейм данных был быть переданным в него (чтобы он мог возвращать весь фрейм данных только со строками удален на основе выбранного атрибута attr).

Я готов услышать предложения по изменению функции remove_outliers также, если они также возвращают весь фрейм данных, как объяснено. Я бы предпочел решения, которые по возможности избегают циклов (если только это не неизбежно и в базовом R / dplyr нет более эффективных методов).

(2) Есть ли простой способ объединить соображения выбросов в нескольких столбцах? например, удалите из фрейма данных df те строки, которые являются выбросами по крайней мере по $ N $ атрибутам из указанного вектора атрибутов / индексов столбца (length≥N). или более сложное условие, например, удалить из фрейма данных df те строки, которые являются выбросами по атрибуту 1 и по крайней мере 2 из атрибутов 2,4,6,8.

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

1 Ответ

1 голос
/ 05 мая 2020

Хорошо - часть 1 (и стараемся по возможности избегать циклов):

Вот некоторые тестовые данные:

test_data=data.frame(
    group=c(rep("a",100),rep("b",100)),
    value=rnorm(200)
)

Мы найдем группы:

groups=levels(test_data[,1]) # or unique(test_data[,1]) if it isn't a factor

И мы рассчитаем пределы выбросов (здесь я указываю только 1 SD) - извините за l oop, но это только по группам, а не по данным:

outlier_sds=1
outlier_limits=sapply(groups,function(g) {
    m=mean(test_data[test_data[,1]==g,2])
    s=sd(test_data[test_data[,1]==g,2])
    return(c(m-outlier_sds*s,m+outlier_sds*s))
})

Таким образом, мы можем определить пределы для каждой строки test_data:

test_data_limits=outlier_limits[,test_data[,1]]

И использовать это для определения выбросов:

outliers=test_data[,2]<test_data_limits[1,] | test_data[,2]>test_data_limits[2,]

(или, комбинируя эти последние шаги):

outliers=test_data[,2]<outlier_limits[1,test_data[,1]] | test_data[,2]>outlier_limits[2,test_data[,1]]

Наконец:

test_data_without_outliers=test_data[!outliers,]

РЕДАКТИРОВАТЬ: теперь часть 2 (примените часть 1 с помощью al oop ко всем столбцам данных):

Некоторые тесты данные с более чем одним столбцом значений:

test_data2=data.frame(
    group=c(rep("a",100),rep("b",100)),
    value1=rnorm(200),
    value2=2*rnorm(200),
    value3=3*rnorm(200)
)

Объедините все шаги части 1 в новую функцию find_outliers, которая возвращает логический вектор, указывающий, является ли какое-либо значение выбросом для соответствующего столбца и группы :

find_outliers = function(values,n_sds,groups) {
    group_names=levels(groups)
    outlier_limits=sapply(group_names,function(g) {
        m=mean(values[groups==g])
        s=sd(values[groups==g])
        return(c(m-n_sds*s,m+n_sds*s))
    })
    return(values < outlier_limits[1,groups] | values > outlier_limits[2,groups])
}

А затем примените эту функцию к каждому из столбцов данных:

test_groups=test_data2[,1]
test_data_outliers=apply(test_data2[,-1],2,function(d) find_outliers(values=d,n_sds=1,groups=test_groups))

rowSums из test_data_outliers указывает, сколько времени Каждая строка считается «выбросом» в различных столбцах по отношению к своей собственной группе:

rowSums(test_data_outliers)
...