从长到宽的格式:保持行顺序并只使用部分行值作为新创建的列名
Long to wide format: keep row orders and use only part of row values for newly created column names
我的数据:
> print(DT)
scenario hyear P
1: flux_0_P1.0_1 2013-2014 0.14044214
2: flux_0_P1.0_1 2014-2015 0.09141671
3: flux_0_P1.0_2 2013-2014 0.69610343
4: flux_0_P1.0_2 2014-2015 0.52359157
5: flux_0_P1.0_3 2013-2014 0.89724457
6: flux_0_P1.0_3 2014-2015 0.78003786
7: flux_0_P1.0_10 2013-2014 0.73752843
8: flux_0_P1.0_10 2014-2015 0.62216371
9: flux_0_P1.0_11 2013-2014 0.14259943
10: flux_0_P1.0_11 2014-2015 0.15309200
11: flux_0_P1.0_12 2013-2014 0.81472886
12: flux_0_P1.0_12 2014-2015 0.66015071
我想从长格式更改为宽格式:
将行顺序保留在新创建的宽数据框 (data.table) 列中的 scenario
列中,例如1, 2, 3, 10, 11, 12
不是 1, 10, 11, 12, 2, 3
仅使用 scenario
列中的部分行值(匹配和替换模式)作为宽数据框 (data.table) 中的列名,例如从 flux_0_P1.0_1
到 P_0_P1.0_1
(P
是原始数据框中值列的名称)
hyear P_0_P1.0_1 P_0_P1.0_2 P_0_P1.0_3 P_0_P1.0_10 P_0_P1.0_11 P_0_P1.0_12
1 2013-2014 0.140 0.696 0.897 0.738 0.143 0.815
2 2014-2015 0.0914 0.524 0.780 0.622 0.153 0.660
我目前的尝试:spread
和dcast
都改变了key
列的顺序
### tidyverse
DT_wide_tidyr <- tidyr::spread(DT, scenario, P)
DT_wide_tidyr
> DT_wide_tidyr
# A tibble: 2 x 7
hyear flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2013-2014 0.140 0.738 0.143 0.815 0.696 0.897
2 2014-2015 0.0914 0.622 0.153 0.660 0.524 0.780
### data.table
DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = "P")
DT_wide_dcast
> DT_wide_dcast
hyear flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
1: 2013-2014 0.14044214 0.7375284 0.1425994 0.8147289 0.6961034 0.8972446
2: 2014-2015 0.09141671 0.6221637 0.1530920 0.6601507 0.5235916 0.7800379
使用的数据
> dput(as.data.frame(DT))
structure(list(scenario = c("flux_0_P1.0_1", "flux_0_P1.0_1",
"flux_0_P1.0_2", "flux_0_P1.0_2", "flux_0_P1.0_3", "flux_0_P1.0_3",
"flux_0_P1.0_10", "flux_0_P1.0_10", "flux_0_P1.0_11", "flux_0_P1.0_11",
"flux_0_P1.0_12", "flux_0_P1.0_12"), hyear = c("2013-2014", "2014-2015",
"2013-2014", "2014-2015", "2013-2014", "2014-2015", "2013-2014",
"2014-2015", "2013-2014", "2014-2015", "2013-2014", "2014-2015"
), P = structure(c(0.140442142857143, 0.0914167142857143, 0.696103428571428,
0.523591571428571, 0.897244571428571, 0.780037857142857, 0.737528428571428,
0.622163714285714, 0.142599428571429, 0.153092, 0.814728857142857,
0.660150714285714))), .Names = c("scenario",
"hyear", "P"), class = "data.frame", row.names = c(NA, -12L))
如有任何建议,我们将不胜感激!谢谢,新年快乐!
编辑
基于@G提供的解决方案。 Grothendieck,这就是我最终使用的:
# Set row order in scenario column
DT[, scenario := factor(scenario, levels = unique(scenario))]
# tidyr
DT_wide_tidyr <- tidyr::spread(DT, scenario, P) %>%
dplyr::rename_at(vars(contains("flux")), funs(sub("flux", names(DT)[3], .)))
DT_wide_tidyr
# data.table
DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = names(DT)[3])
names(DT_wide_dcast) <- gsub("flux", names(DT)[3], names(DT_wide_dcast))
DT_wide_dcast
解决方案
DT$scenario <- gsub('flux_', 'P_', DT$scenario)
DT$scenario <- gsub('(?<=0_)(\d)$', '0\1', DT$scenario, perl = TRUE)
DT <- tidyr::spread(DT, scenario, P)
结果
hyear P_0_P1.0_01 P_0_P1.0_02 P_0_P1.0_03 P_0_P1.0_10 P_0_P1.0_11
1 2013-2014 0.14044214 0.6961034 0.8972446 0.7375284 0.1425994
2 2014-2015 0.09141671 0.5235916 0.7800379 0.6221637 0.1530920
P_0_P1.0_12
1 0.8147289
2 0.6601507
说明
您的问题是因为按字母顺序排列“1”、“2”、“10”会导致“1”、“10”、“2”。如果添加前导零,此问题就会消失。
更新
您可以使用以下函数对此进行概括:
custom_spread <- function(data, key, value, strip_name = NULL) {
if ( !is.null(strip_name) ) {
data[, key] <- gsub(strip_name, key, data[, key])
}
data[, key] <- gsub('(?<=0_)(\d)$', '0\1', data[, key], perl = TRUE)
data <- tidyr::spread(data, key, value)
colnames(data) <- gsub('(?<=0_)0(\d)$', '\1', colnames(data), perl = TRUE)
return(data)
}
例如,将它用于您的问题:
custom_spread(DT, 'scenario', 'P', strip_name = 'flux')
仍然给出相同的结果:
hyear scenario_0_P1.0_1 scenario_0_P1.0_2 scenario_0_P1.0_3
1 2013-2014 0.14044214 0.6961034 0.8972446
2 2014-2015 0.09141671 0.5235916 0.7800379
scenario_0_P1.0_10 scenario_0_P1.0_11 scenario_0_P1.0_12
1 0.7375284 0.1425994 0.8147289
2 0.6221637 0.1530920 0.6601507
但是您可以将它用于任何其他值列名称,例如 "T"、"U" 等。这也会删除添加的前导零以使列排序正确。如果要保留前导零,只需注释掉 return()
.
之前的行
如果将 scenario
列更改为具有所需顺序的给定水平的因子,那么您的两个解决方案都会起作用。
如果 DF
是问题末尾显示的输入,则将您的代码与此处显示的 DF2
一起使用:
DF2 <- transform(DF, scenario = factor(scenario, levels = unique(scenario)))
如果 wide
是您的代码的结果,那么这会将列名称中的 flux
更改为 P
:
names(wide) <- sub("flux", "P", names(wide))
我的数据:
> print(DT)
scenario hyear P
1: flux_0_P1.0_1 2013-2014 0.14044214
2: flux_0_P1.0_1 2014-2015 0.09141671
3: flux_0_P1.0_2 2013-2014 0.69610343
4: flux_0_P1.0_2 2014-2015 0.52359157
5: flux_0_P1.0_3 2013-2014 0.89724457
6: flux_0_P1.0_3 2014-2015 0.78003786
7: flux_0_P1.0_10 2013-2014 0.73752843
8: flux_0_P1.0_10 2014-2015 0.62216371
9: flux_0_P1.0_11 2013-2014 0.14259943
10: flux_0_P1.0_11 2014-2015 0.15309200
11: flux_0_P1.0_12 2013-2014 0.81472886
12: flux_0_P1.0_12 2014-2015 0.66015071
我想从长格式更改为宽格式:
将行顺序保留在新创建的宽数据框 (data.table) 列中的
scenario
列中,例如1, 2, 3, 10, 11, 12
不是1, 10, 11, 12, 2, 3
仅使用
scenario
列中的部分行值(匹配和替换模式)作为宽数据框 (data.table) 中的列名,例如从flux_0_P1.0_1
到P_0_P1.0_1
(P
是原始数据框中值列的名称)hyear P_0_P1.0_1 P_0_P1.0_2 P_0_P1.0_3 P_0_P1.0_10 P_0_P1.0_11 P_0_P1.0_12 1 2013-2014 0.140 0.696 0.897 0.738 0.143 0.815 2 2014-2015 0.0914 0.524 0.780 0.622 0.153 0.660
我目前的尝试:spread
和dcast
都改变了key
列的顺序
### tidyverse
DT_wide_tidyr <- tidyr::spread(DT, scenario, P)
DT_wide_tidyr
> DT_wide_tidyr
# A tibble: 2 x 7
hyear flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2013-2014 0.140 0.738 0.143 0.815 0.696 0.897
2 2014-2015 0.0914 0.622 0.153 0.660 0.524 0.780
### data.table
DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = "P")
DT_wide_dcast
> DT_wide_dcast
hyear flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
1: 2013-2014 0.14044214 0.7375284 0.1425994 0.8147289 0.6961034 0.8972446
2: 2014-2015 0.09141671 0.6221637 0.1530920 0.6601507 0.5235916 0.7800379
使用的数据
> dput(as.data.frame(DT))
structure(list(scenario = c("flux_0_P1.0_1", "flux_0_P1.0_1",
"flux_0_P1.0_2", "flux_0_P1.0_2", "flux_0_P1.0_3", "flux_0_P1.0_3",
"flux_0_P1.0_10", "flux_0_P1.0_10", "flux_0_P1.0_11", "flux_0_P1.0_11",
"flux_0_P1.0_12", "flux_0_P1.0_12"), hyear = c("2013-2014", "2014-2015",
"2013-2014", "2014-2015", "2013-2014", "2014-2015", "2013-2014",
"2014-2015", "2013-2014", "2014-2015", "2013-2014", "2014-2015"
), P = structure(c(0.140442142857143, 0.0914167142857143, 0.696103428571428,
0.523591571428571, 0.897244571428571, 0.780037857142857, 0.737528428571428,
0.622163714285714, 0.142599428571429, 0.153092, 0.814728857142857,
0.660150714285714))), .Names = c("scenario",
"hyear", "P"), class = "data.frame", row.names = c(NA, -12L))
如有任何建议,我们将不胜感激!谢谢,新年快乐!
编辑
基于@G提供的解决方案。 Grothendieck,这就是我最终使用的:
# Set row order in scenario column
DT[, scenario := factor(scenario, levels = unique(scenario))]
# tidyr
DT_wide_tidyr <- tidyr::spread(DT, scenario, P) %>%
dplyr::rename_at(vars(contains("flux")), funs(sub("flux", names(DT)[3], .)))
DT_wide_tidyr
# data.table
DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = names(DT)[3])
names(DT_wide_dcast) <- gsub("flux", names(DT)[3], names(DT_wide_dcast))
DT_wide_dcast
解决方案
DT$scenario <- gsub('flux_', 'P_', DT$scenario)
DT$scenario <- gsub('(?<=0_)(\d)$', '0\1', DT$scenario, perl = TRUE)
DT <- tidyr::spread(DT, scenario, P)
结果
hyear P_0_P1.0_01 P_0_P1.0_02 P_0_P1.0_03 P_0_P1.0_10 P_0_P1.0_11
1 2013-2014 0.14044214 0.6961034 0.8972446 0.7375284 0.1425994
2 2014-2015 0.09141671 0.5235916 0.7800379 0.6221637 0.1530920
P_0_P1.0_12
1 0.8147289
2 0.6601507
说明
您的问题是因为按字母顺序排列“1”、“2”、“10”会导致“1”、“10”、“2”。如果添加前导零,此问题就会消失。
更新
您可以使用以下函数对此进行概括:
custom_spread <- function(data, key, value, strip_name = NULL) {
if ( !is.null(strip_name) ) {
data[, key] <- gsub(strip_name, key, data[, key])
}
data[, key] <- gsub('(?<=0_)(\d)$', '0\1', data[, key], perl = TRUE)
data <- tidyr::spread(data, key, value)
colnames(data) <- gsub('(?<=0_)0(\d)$', '\1', colnames(data), perl = TRUE)
return(data)
}
例如,将它用于您的问题:
custom_spread(DT, 'scenario', 'P', strip_name = 'flux')
仍然给出相同的结果:
hyear scenario_0_P1.0_1 scenario_0_P1.0_2 scenario_0_P1.0_3
1 2013-2014 0.14044214 0.6961034 0.8972446
2 2014-2015 0.09141671 0.5235916 0.7800379
scenario_0_P1.0_10 scenario_0_P1.0_11 scenario_0_P1.0_12
1 0.7375284 0.1425994 0.8147289
2 0.6221637 0.1530920 0.6601507
但是您可以将它用于任何其他值列名称,例如 "T"、"U" 等。这也会删除添加的前导零以使列排序正确。如果要保留前导零,只需注释掉 return()
.
如果将 scenario
列更改为具有所需顺序的给定水平的因子,那么您的两个解决方案都会起作用。
如果 DF
是问题末尾显示的输入,则将您的代码与此处显示的 DF2
一起使用:
DF2 <- transform(DF, scenario = factor(scenario, levels = unique(scenario)))
如果 wide
是您的代码的结果,那么这会将列名称中的 flux
更改为 P
:
names(wide) <- sub("flux", "P", names(wide))