Сгруппируйте и создайте три новых столбца по условию [Low, Hit, High] (эквивалент панд в R)

Я хочу адаптировать некоторые части моего кода Python к R и не знаю, как это решить.

У меня есть большой набор данных (~ 5 миллионов строк) с результатами некоторых тренировок по машинному обучению. Теперь я хочу квалифицировать результаты, определив, попадают ли они в «целевой диапазон». Этот «целевой диапазон» содержит все значения между -0.25 и +0.25. Если он находится внутри этого диапазона, это Hit, если он ниже Low и High с другой стороны.

df = data.frame(Type=c("RF", "RF", "RF", "MLP", "MLP", "MLP"),
               Value=c(-1.5, -0.1, 1.7, 0.2, -0.7, -0.6))
                
df

+--------+---------+
| Type   |   Value |
+--------+---------|
| RF     |    -1.5 | <- Low
| RF     |    -0.1 | <- Hit
| RF     |     1.7 | <- High
| MLP    |     0.2 | <- Hit
| MLP    |    -0.7 | <- Low
| MLP    |    -0.6 | <- Low
+--------+---------+

В python python pandas я мог бы решить это с помощью:

(df.assign(group=pd.cut(df['Value'],
               [float('-inf'), -0.25, 0.25, float('inf')],
                        labels=['Low', 'Hit', 'High']))
   .pivot_table(index='Type', columns='group', values='Value', aggfunc='count')
   .reset_index()
   .rename_axis(None, axis=1)
)

Может ли кто-нибудь помочь мне, как я могу адаптировать это к R? native R, data.table или dplyr не имеет значения, я открыт для любого подхода. Тем не менее чем быстрее, тем лучше :-)


Ожидаемый результат

df_expected = data.frame(Type=c("RF", "MLP"), "Low"=c(1,2), "Hit"=c(1,1), "High"=c(1,0))

+--------+-------+-------+--------+
| Type   |   Low |   Hit |   High |
+--------+-------+-------+--------|
| RF     |     1 |     1 |      1 |
| MLP    |     2 |     1 |      0 |
+--------+-------+-------+--------+
Стоит ли изучать 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
0
112
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Команда base::cut() может сделать это:

 df$Cut<-cut(df$Value, breaks = c(-Inf, -0.25, 0.25, Inf), labels = c("Low", "Hit", "High"), )

 df
  Type Value  Cut
1   RF  -1.5  Low
2   RF  -0.1  Hit
3   RF   1.7 High
4  MLP   0.2  Hit
5  MLP  -0.7  Low
6  MLP  -0.6  Low

 table(df$Type, df$Cut)
     
      Low Hit High
  MLP   2   1    0
  RF    1   1    1

Редактировать 2: цепочка dplyr с количеством и pivot_wider:

df %>% 
  mutate(name = cut(Value, 
                   breaks = c(-Inf, -0.25, 0.25, Inf), 
                   labels = c("Low", "Hit", "High"), )) %>% 
  count(Type, name) %>% 
  pivot_wider(values_from = n,
              values_fill = 0)

Редактировать 3: без подсчета и с использованием value_fn (функция значения)

df %>% 
  mutate(name = cut(Value, 
                   breaks = c(-Inf, -0.25, 0.25, Inf), 
                   labels = c("Low", "Hit", "High"), )) %>% 
  pivot_wider(values_from = Value,
              values_fn = length,
              values_fill = 0)

Вот совершенно другой подход, использующий dplyover::over() с именованным списком перекодирования. Часть перекодирования немного более трудоемка по сравнению с cut(), но нам не нужно прямоугольное преобразование данных с pivot_wider().

library(dplyr)
library(dplyover) # https://timteafan.github.io/dplyover/
# disclaimer: I'm the maintainer and its not on CRAN

df %>% 
  group_by(Type) %>% 
  summarise(over(list(Low  = c(-Inf, -0.25), 
                      Hit  = c(-0.25, 0.25),
                      High = c(0.25, Inf)),
                 ~ sum(Value > .x[1] & Value < .x[2])
                 )
  )
#> # A tibble: 2 × 4
#>   Type    Low   Hit  High
#>   <chr> <int> <int> <int>
#> 1 MLP       2     1     0
#> 2 RF        1     1     1

Мы также можем использовать cut() для части перекодирования, а затем использовать dplyover::over().

df %>% 
  mutate(Value = cut(Value,
                     breaks = c(-Inf, -0.25, 0.25, Inf),
                     labels = c("Low", "Hit", "High"))) %>% 
  group_by(Type) %>% 
  summarise(over(unique(.$Value),
                 ~ sum(Value  == .x)
                 )
  )
#> # A tibble: 2 × 4
#>   Type    Low   Hit  High
#>   <chr> <int> <int> <int>
#> 1 MLP       2     1     0
#> 2 RF        1     1     1

данные ОП

df = data.frame(Type=c("RF", "RF", "RF", "MLP", "MLP", "MLP"),
                Value=c(-1.5, -0.1, 1.7, 0.2, -0.7, -0.6))

Created on 2022-10-15 by the reprex package (v0.3.0)

Большое спасибо за вашу помощь и ваше объяснение, если бы я мог, я бы принял все решения. голосовать за

Marco_CH 17.10.2022 08:25
Ответ принят как подходящий

Я вижу, что ваши данные несколько велики. Я бы использовал data.table для того, что вам нужно, так как вы получите результаты намного быстрее.

Вот как я бы построил сценарий, чтобы добиться того, чего вы хотите. Здесь используются оптимизированные функции data.table (работает как CASE WHEN в SQL) и fcase, а я также создаю класс dcast для учета наблюдений, которые не подпадают под ваши предопределенные условия.

library(data.table)

df = data.frame(Type=c("RF", "RF", "RF", "MLP", "MLP", "MLP"),
                Value=c(-1.5, -0.1, 1.7, 0.2, -0.7, -0.6))

df = as.data.table(df)

df[, class := fcase(Value >= -0.25 & Value <= 0.25, "Hit",
                    Value < -0.25, "Low",
                    Value > 0.25, "High",
                    default = "error")]

dcast(df, Type ~ class, value.var = "class")

Вывод:

   Type High Hit Low
1:  MLP    0   1   2
2:   RF    1   1   1

Другое решение:

library(data.table)

cases = factor(1:3, 1:3, c("Low", "Hit", "High"))
dcast(setDT(df), Type ~ cases[findInterval(Value, c(-Inf, -.25, .25), TRUE)], fun=length)

     Type   Low   Hit  High
1:    MLP     2     1     0
2:     RF     1     1     1

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