У меня есть фрейм данных, который мне нужно преобразовать. Мне нужно изменить уникальные строки на отдельные столбцы на основе значения столбца.
Мои данные ниже:
df1 <- data.frame(V1 = c("a", "a", "b", "b","b"),
V2 = c("product1", "transport", "product1", "product2","transport"),
V3 = c("100", "10", "100", "100","10"))
> df1
V1 V2 V3
1 a product1 100
2 a transport 10
3 b product1 100
4 b product2 100
5 b transport 10
Мне нужно следующее преобразование и деление стоимости V3 на количество продуктов, входящих в V1.
> df2
V1 V2 transport V3
1 a product1 10 100
2 b product1 5 100
3 b product2 5 100
Вот один из способов с data.table
— преобразовать в data.table
(setDT
), убедиться, что «V3» — это numeric
(для деления — он был создан как символ), сгруппировать по «V1», создать «транспорт», извлекая «V3 ' значение, где "V2" означает "транспорт" и разделить на количество элементов в "V2", которые не являются "транспортом", затем подмножить данные, удалив элементы "транспорт" из "V2"
library(data.table)
df1$V3 <- as.numeric(df1$V3)
setDT(df1)[, transport := V3[V2 == "transport"]/
sum(V2 != "transport"), by = V1]
df1[V2 != "transport"]
V1 V2 V3 transport
<char> <char> <num> <num>
1: a product1 100 10
2: b product1 100 5
3: b product2 100 5
Или другой вариант с dplyr/tidyr
library(dplyr)
library(tidyr)
df1 %>%
type.convert(as.is = TRUE) %>%
mutate(transport = case_when(V2 == 'transport' ~ V3)) %>%
group_by(V1) %>%
fill(transport, .direction = "downup") %>%
mutate(transport = transport/sum(V2 != "transport")) %>%
ungroup %>%
filter(V2 != "transport")
# A tibble: 3 × 4
V1 V2 V3 transport
<chr> <chr> <int> <dbl>
1 a product1 100 10
2 b product1 100 5
3 b product2 100 5
Вот решение на основе tidyverse с использованием tidyr
и dplyr
:
V3
в числовойtransport
, затем поверните длиннее, чтобы свернуть "product1"
и "product2"
обратно в один столбецtransport
на количество случаев в V1
.library(dplyr)
library(tidyr)
df1 %>%
mutate(V3 = as.numeric(V3)) %>%
pivot_wider(names_from = V2, values_from = V3) %>%
pivot_longer(
cols = c(product1, product2),
names_to = "V2",
values_to = "V3",
values_drop_na = TRUE
) %>%
group_by(V1) %>%
mutate(transport = transport / n()) %>%
ungroup()
#> # A tibble: 3 x 4
#> V1 transport V2 V3
#> <chr> <dbl> <chr> <dbl>
#> 1 a 10 product1 100
#> 2 b 5 product1 100
#> 3 b 5 product2 100
Created on 2022-03-17 by the reprex package (v2.0.1)
Вот еще один подход к получению желаемого результата, если я правильно понял вашу точку зрения:
library(dplyr)
library(tidyr)
df1 %>%
group_by(V1) %>%
mutate(V3 = ifelse(V2 == "transport", as.numeric(V3) / (n() - 1), as.numeric(V3))) %>%
ungroup() %>%
pivot_wider(names_from = V2, values_from = V3) %>%
pivot_longer(c(product1, product2), names_to = "V2", values_to = "V3") %>%
drop_na()
# A tibble: 3 x 4
V1 transport V2 V3
<chr> <dbl> <chr> <dbl>
1 a 10 product1 100
2 b 5 product1 100
3 b 5 product2 100
Другое возможное решение:
library(tidyverse)
df1 %>%
mutate(V3 = as.numeric(V3)) %>%
group_by(V1) %>%
mutate(transport = if_else(V2 == "transport", V3 / (n()-1), NA_real_)) %>%
fill(transport, .direction = "up") %>% ungroup %>%
filter(V2 != "transport")
#> # A tibble: 3 × 4
#> V1 V2 V3 transport
#> <chr> <chr> <dbl> <dbl>
#> 1 a product1 100 10
#> 2 b product1 100 5
#> 3 b product2 100 5
Вот еще один с pivoting
:
library(dplyr)
library(tidyr)
df1 %>%
pivot_wider(
names_from = V2,
values_from = V3
) %>%
pivot_longer(
-c(V1, transport),
names_to = "V2",
values_to = "V3"
)%>%
type.convert(as.is = TRUE) %>%
na.omit() %>%
group_by(V1) %>%
mutate(transport = transport/max(row_number()))
V1 transport V2 V3
<chr> <dbl> <chr> <int>
1 a 10 product1 100
2 b 5 product1 100
3 b 5 product2 100
Рад тебя видеть, дорогой @Anoushiravan!!!
Вот подход data.table:
f <- function(p,v) {
ps=grepl("^p",p)
list(V2 = p[ps], transport=rep(v[!ps]/sum(ps), sum(ps)), V3 = v[ps])
}
setDT(df1)[,f(V2,as.numeric(V3)), V1 ]
V1 V2 transport V3
<char> <char> <num> <num>
1: a product1 10 100
2: b product1 5 100
3: b product2 5 100
Мужик, так рад тебя здесь видеть :)