我有一个宽格式的数据框,大约有100,000多行和700多列。我需要计算单个列作为其前一列的比率。以下模拟我的问题
df = data.frame(
name = c("c1", "c2", "c3"),
m12 = c(50, 150, 200),
m23 = c(100, 180, 120),
m37 = c(150, 414, 180)
)
所需的输出数据帧(dfo)将是
name m12 r2 r3
1 c1 50 2.0 1.5
2 c2 150 1.2 2.3
3 c3 200 0.6 1.5
其中
dfo$r2 = df$m23/df$m12
dfo$r3 = df$m37/df$m23
我需要的是识别给定列位置获取前一列并使用它来计算比率。使用for循环,我可以得到我正在寻找的东西,但那不是" R方式"做事。
我如何获得R-ish解决方案?我的偏好是使用tidyverse或base R方式,但我也对其他方法持开放态度。它应该以一种通用的方式在具有给定结构的任意数量的行或列的数据框上工作。
答案 0 :(得分:2)
您可以使用lapply
计算列,然后将它们绑定到现有的data.frame。
dfo = cbind(df, lapply(3:ncol(df), function(i) df[,i]/df[,i-1]))
names(dfo)[5:6] = c("r2", "r3")
dfo
name m12 m23 m37 r2 r3
1 c1 50 100 150 2.0 1.5
2 c2 150 180 414 1.2 2.3
3 c3 200 120 180 0.6 1.5
答案 1 :(得分:1)
您可以使用基本的R这样做:
df = data.frame(
name = c("c1", "c2", "c3"),
m12 = c(50, 150, 200),
m23 = c(100, 180, 120),
m37 = c(150, 414, 180)
)
# Get the index of all columns that start with "m"
z = which(grepl("^m",colnames(df)))
# calculate the proportion to the previous column
proportions = df[,z[-1]]/df[,z[-length(z)]]
结果:
> proportions
m23 m37
1 2.0 1.5
2 1.2 2.3
3 0.6 1.5
计算新列后,使用colnames
newName = paste0("r",2:length(z))
colnames(proportions) = newName
> proportions
r2 r3
1 2.0 1.5
2 1.2 2.3
3 0.6 1.5
答案 2 :(得分:0)
如果你想以整齐的方式做这件事,你应该先将gather()
列放入行中:
library(dplyr, warn.conflicts = FALSE)
library(tidyr)
df = data.frame(
name = c("c1", "c2", "c3"),
m12 = c(50, 150, 200),
m23 = c(100, 180, 120),
m37 = c(150, 414, 180)
)
df_gathered <- gather(df, "key", "value", starts_with("m"))
df_gathered
#> name key value
#> 1 c1 m12 50
#> 2 c2 m12 150
#> 3 c3 m12 200
#> 4 c1 m23 100
#> 5 c2 m23 180
#> 6 c3 m23 120
#> 7 c1 m37 150
#> 8 c2 m37 414
#> 9 c3 m37 180
然后,您可以像往常一样使用lag()
,因为列现在是行。虽然我的代码在这里可能不够智能,但如果您熟悉争论行,则应该更容易使用这种形式的数据:
df_normalized <- df_gathered %>%
group_by(name) %>%
mutate(value_normalized = value / lag(value),
# treat the first item (m12) differently
key = if_else(is.na(value_normalized), key, paste0("r", row_number() - 1L)),
value = if_else(is.na(value_normalized), value, value_normalized)) %>%
select(-value_normalized)
df_normalized
#> # A tibble: 9 x 3
#> # Groups: name [3]
#> name key value
#> <fctr> <chr> <dbl>
#> 1 c1 m12 50.0
#> 2 c2 m12 150.0
#> 3 c3 m12 200.0
#> 4 c1 r1 2.0
#> 5 c2 r1 1.2
#> 6 c3 r1 0.6
#> 7 c1 r2 1.5
#> 8 c2 r2 2.3
#> 9 c3 r2 1.5
最后,如果需要,可以将数据spread()
添加到列范围内。
spread(df_normalized, key, value)
#> # A tibble: 3 x 4
#> # Groups: name [3]
#> name m12 r1 r2
#> * <fctr> <dbl> <dbl> <dbl>
#> 1 c1 50 2.0 1.5
#> 2 c2 150 1.2 2.3
#> 3 c3 200 0.6 1.5
答案 3 :(得分:0)
我们可以使用dplyr
和purrr
中的函数。我们的想法是将数据帧转换为列表进行操作,然后将其转换回数据帧。
library(dplyr)
library(purrr)
df2 <- df %>% select(-name)
df3 <- map2_dfc(df2[-1], df2[-ncol(df2)], ~.x/.y) %>%
setNames(paste0("r", 2:ncol(df2)))
df4 <- bind_cols(df, df3)
df4
# name m12 m23 m37 r2 r3
# 1 c1 50 100 150 2.0 1.5
# 2 c2 150 180 414 1.2 2.3
# 3 c3 200 120 180 0.6 1.5
来自dplyr
和tidyr
的解决方案。它使用gather
将数据框从宽格式转换为长格式,使用mutate
和lag
计算值,然后重新排列列。最后,将其转换回宽格式。 df3
是最终输出。
library(dplyr)
library(tidyr)
df2 <- df %>%
gather(M, value1, -name) %>%
arrange(name, M) %>%
group_by(name) %>%
mutate(value2 = value1/lag(value1)) %>%
mutate(R = paste0("r", 1:n()))
df3 <- bind_rows(df2 %>% select(name, column = M, value = value1),
df2 %>% select(name, column = R, value = value2)) %>%
drop_na(value) %>%
spread(column, value)
df3
# # A tibble: 3 x 6
# # Groups: name [3]
# name m12 m23 m37 r2 r3
# * <fctr> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 c1 50 100 150 2.0 1.5
# 2 c2 150 180 414 1.2 2.3
# 3 c3 200 120 180 0.6 1.5