Я хочу адаптировать некоторые части моего кода 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 |
+--------+-------+-------+--------+
Команда 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)
Я вижу, что ваши данные несколько велики. Я бы использовал 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
Большое спасибо за вашу помощь и ваше объяснение, если бы я мог, я бы принял все решения. голосовать за