Я хотел бы вычесть среднее значение каждого из «пустых» столбцов из каждого значения в предыдущих трех столбцах в наборе данных, который выглядит примерно так:
df <- data.frame(da=1:5, d2=6:10, dd=2:6,
blank...1=c(0.1, 0.1, 0.4, 0.2, 0.1), d5=2:6, dg=7:11,
di=3:7, blank...2=c(0.2, 0.2, 0.4, 0.1, 0.1), dm=21:25,
h4=5:9, d7=26:30, blank...3=c(0.1, 0.3, 0.4, 0.4, 0.1))
df
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 1 6 2 0.1 2 7 3 0.2 21 5 26 0.1
# 2 2 7 3 0.1 3 8 4 0.2 22 6 27 0.3
# 3 3 8 4 0.4 4 9 5 0.4 23 7 28 0.4
# 4 4 9 5 0.2 5 10 6 0.1 24 8 29 0.4
# 5 5 10 6 0.1 6 11 7 0.1 25 9 30 0.1
Другими словами, я хочу вычесть среднее значение пробела...1 из каждого значения столбцов da, d2 и dd; затем вычтите среднее значение пробела...2 из каждого значения столбцов d5, dg и di и так далее. Фактический набор данных на самом деле содержит 15 пробелов, которые необходимо вычесть из предыдущих 11 столбцов.
Я сгенерировал средние значения всех «пустых» столбцов в векторе, используя colMeans
, но тогда я не знаю, как написать функцию, чтобы указать, какое значение использовать для каких столбцов.
Как это можно сделать с помощью функции?
Я уверен, что есть более элегантное решение, но в базе R вы можете сначала определить столбцы с «пробелом» в имени (blnknms
), затем использовать lapply
для перебора этих столбцов и вычесть среднее значение этого столбца из трех предыдущих столбцов. :
# get columns with "blank" in the name
blnknms <- grep("blank", names(df))
df[,-blnknms] <- unlist(lapply(blnknms, \(x){
df[,(x-3):(x-1)] - mean(df[,x])
}), recursive = FALSE)
Выход:
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
# 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
# 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
# 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
# 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1
Спасибо AC_06 — на моей стороне он работает отлично (я просто скопировал и вставил его с нуля), поэтому не уверен, в чем проблема.
Вот еще один базовый подход R, вдохновленный jpsmith:
blanks = grep("blank", names(df)) # find the blank columns
blank_means = colMeans(df[blanks]) # get their means
## subtract blanks means from the non-blank columns
## this relies on having exactly 3 non-blank columns per blank column
df[-blanks] = Map("-", df[-blanks], rep(blank_means, each = 3))
df
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
# 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
# 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
# 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
# 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1
Вот более подробный подход tidyverse. Сначала я делаю данные длинными, отслеживая исходную строку и то, какую группу «пустых» должен использовать каждый столбец.
library(tidyverse)
df_long <- df |>
mutate(row = row_number()) |>
pivot_longer(-row) |>
mutate(group = cumsum(lag(name |> str_detect("blank"), 1, 0)), .by = row)
Затем я могу изменить каждое значение, чтобы вычесть соответствующее «пустое» среднее значение (путем вычитания среднего значения в группе, имя которой содержит «пусто») и снова изменить широкую форму.
df_long |>
mutate(value = if_else(name |> str_detect("blank"), value,
value - mean(value[name |> str_detect("blank")])),
.by = group) |>
select(-group) |>
pivot_wider(names_from = name, values_from = value)
Результат
# A tibble: 5 × 13
row da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
<int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.7 4.74 25.7 0.1
2 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.7 5.74 26.7 0.3
3 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.7 6.74 27.7 0.4
4 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.7 7.74 28.7 0.4
5 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.7 8.74 29.7 0.1
Если вы rep
съедаете каждый из colMeans
столько раз, сколько столбцов есть в соответствующих непустых* срезах, вы можете просто вычесть результирующий вектор из срезов в целом. Обратите внимание: чтобы вычесть вектор из столбцов, нам нужно t
транспонировать и повторно t
транспонировать.
> bl <- grepl('^blank', names(df))
> df[!bl] <- t(t(df[!bl]) - rep(colMeans(df[bl]), each=which.max(bl) - 1L))
> df
da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1
Это позволяет избежать циклов (включая lapply
и Map
) или изменения формы и, следовательно, более эффективно.
Похоже, что эти обширные данные требуют сделать длинными, что сделало бы операцию тривиальной. Я бы предложил собирать длинные данные на ранних этапах анализа и снова расширять их только для отчетов как можно позже, если это необходимо.