Как вычесть средние значения определенных столбцов из других предыдущих столбцов?

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

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, но тогда я не знаю, как написать функцию, чтобы указать, какое значение использовать для каких столбцов.

Как это можно сделать с помощью функции?

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

moodymudskipper 23.04.2024 21:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
81
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я уверен, что есть более элегантное решение, но в базе 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 — на моей стороне он работает отлично (я просто скопировал и вставил его с нуля), поэтому не уверен, в чем проблема.

jpsmith 24.04.2024 02:38
Ответ принят как подходящий

Вот еще один базовый подход 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) или изменения формы и, следовательно, более эффективно.

Другие вопросы по теме