嵌套 For 循环转置和整理重复输入的数据 table
Nested For Loops to transpose and tidy data table of duplicate inputs
不确定如何简洁地表达这个问题,所以标题可能不好,答案可能已经存在。我基本上在使用嵌套 for 循环将值插入新数据帧时遇到问题。
我有一个大型数据文件,其中包含重复的名称以及相关的投资和奖金,需要重新排列以创建一个新的 Table/dataframe,其中包含每个相关列的总计。这是我拥有的数据 table 的简单说明:
Test Data
我用测试数据编写了以下代码,一个用 Excel 编写并保存为 Book1 的 csv 文件:
Name <- c("Name 1","Name 1","Name 1","Name 1","Name 1","Name 2","Name 2","Name 2",
"Name 3","Name 3","Name 3","Name 3","Name 3","Name 4","Name 4","Name 4")
Initial.Value <- c(142, 847, 982, 867, 898, 437, 169, 478,260, 789, 216, 373, 820,
985, 943, 325)
Bonus.1 <- c(4, 2, 5, 0, 9, 6, 6, 7, 5, 8, 5, 5, 5, 8, 8, 8)
Bonus.2 <- c(4, 4, 0, 10, 5, 10, 2, 10, 8, 3, 9, 6, 3, 3, 2, 1)
Bonus.3 <- c(3, 0, 2, 7, 5, 0, 3, 6, 9, 5, 1, 2, 1, 5, 3, 2)
Bonus.4 <- c(1, 10, 2, 3, 2, 5, 7, 5, 3, 1, 6, 10, 3, 4, 7, 9)
data_file <- data.frame(Name, Initial.Value, Bonus.1, Bonus.2, Bonus.3, Bonus.4)
Rows <- unique(data_file$Name)
Output_file <- data.frame(matrix(0, ncol = length(Rows), nrow = 5))
colnames(Output_file) <- Rows
rownames(Output_file) <- colnames(data_file)[c(2,3,4,5,6)]
for(i in length(Rows)){ # Looks at each name in turn
Indices_Of_Interest <- which(lapply(data_file$Name,
function(x) any(match(x, Rows[i]))) == TRUE)
for(k in length(Output_file[, 1])){ # Goes down the Output_File
row_header <- rownames(Output_file)[k]
col_header <- Rows[i]
Output_file[row_header, col_header] <- sum(data_file[row_header][Indices_Of_Interest, ])
}
}
当我 运行 这段代码逐行工作时,它通过依次更新每个单元格来工作,但是当我 运行 for 循环时,它似乎只作用于最后一个单元格,留下另一个单元格为零,如下:
Output file, correct structure but not filled correctly
我不确定“Initial.Value”从何而来,但这为您提供了其他一切。
library(tidyverse)
data_file %>%
group_by(Name) %>%
summarise(across(starts_with("Bonus"), sum), .groups="drop") %>%
pivot_longer(names_to="Index", cols=starts_with("Bonus")) %>%
pivot_wider(values_from="value", names_from="Name")
# A tibble: 4 x 5
Index `Name 1` `Name 2` `Name 3` `Name 4`
<chr> <dbl> <dbl> <dbl> <dbl>
1 Bonus.1 20 19 28 24
2 Bonus.2 23 22 29 6
3 Bonus.3 17 9 18 10
4 Bonus.4 18 17 23 20
回应OP的评论:恕我直言,我认为他们所说的“复杂性”大部分是由于他们的数据格式不“整洁”造成的。 (请参阅我之前的评论和 link。)使用整洁的数据,大部分的复杂性都会消失。我声称 OP 的数据不整洁的原因是列名称中有相关信息:付款类型(“Initial.Value”与“奖金”)和奖金索引。这使生活变得比需要的更加困难。所以,这是一个可能的解决方案,从 OP 的修订测试数据(包括 Initial.Payment
)开始,基于一个可能整洁的数据集。
# Make the data tidy
tidyData <- data_file %>%
pivot_longer(
cols=c(starts_with("Bonus"), "Initial.Value"),
values_to="Value",
names_to="Source")
tidyData %>% head(5)
# A tibble: 5 x 3
Name Source Value
<fct> <chr> <dbl>
1 Name 1 Bonus.1 4
2 Name 1 Bonus.2 4
3 Name 1 Bonus.3 3
4 Name 1 Bonus.4 1
5 Name 1 Initial.Value 142
为什么我说这个格式比原来的格式好?仅仅是因为它使 后面的代码完全独立于奖金数量、支付类型(“Initial.Value”、“Bonus.x”、“其他支付类型”等)等)和不同名称的数量。我相信它在 OP 示例数据的上下文中是整洁的,但不一定在每个上下文中都是整洁的。例如,将 Source
分成两列或更多列可能很有用,例如 PaymentType
和 Index
。 'PaymentTypecould countain
Initial.Paymentor
Bonusand
Indexcould define the
Bonussuffix (and
0,
1or
NAfor
Initial.Payment` 条记录)。例如,这将允许轻松计算总体奖金(同样,独立于奖金类型的数量)。
所以,现在我有了一个整洁的数据集,整理所需的信息很简单:
totalBonus <- tidyData %>%
group_by(Name, Source) %>%
summarise(Value=sum(Value), .groups="drop")
这个数据集仍然很整洁,因此它是进一步操作的最佳选择,但不一定是展示的最佳选择。但这很容易修复。提供 OP 所需的输出:
totalBonus %>%
pivot_wider(names_from=Name, values_from=Value) %>%
arrange(desc(Source))
A tibble: 5 x 5
Source `Name 1` `Name 2` `Name 3` `Name 4`
<chr> <dbl> <dbl> <dbl> <dbl>
1 Initial.Value 3736 1084 2458 2253
2 Bonus.4 18 17 23 20
3 Bonus.3 17 9 18 10
4 Bonus.2 23 22 29 6
5 Bonus.1 20 19 28 24
代码中的错误是 for 循环中的语法。
for(i in length(Rows)){
将启动 for 循环以使用长度为 1 的整数,在上述情况下函数“length(Rows)”returns 为 4 的整数(值 4,长度 1)。因此循环只有一次迭代,因此只填充输出中的最后一个单元格 table.
循环应该按如下方式启动:
for(i in seq(length(Rows)){
嵌套循环也是如此,应该是:
for(k in seq(length(Output_file[, 1]))){
不确定如何简洁地表达这个问题,所以标题可能不好,答案可能已经存在。我基本上在使用嵌套 for 循环将值插入新数据帧时遇到问题。
我有一个大型数据文件,其中包含重复的名称以及相关的投资和奖金,需要重新排列以创建一个新的 Table/dataframe,其中包含每个相关列的总计。这是我拥有的数据 table 的简单说明:
Test Data
我用测试数据编写了以下代码,一个用 Excel 编写并保存为 Book1 的 csv 文件:
Name <- c("Name 1","Name 1","Name 1","Name 1","Name 1","Name 2","Name 2","Name 2",
"Name 3","Name 3","Name 3","Name 3","Name 3","Name 4","Name 4","Name 4")
Initial.Value <- c(142, 847, 982, 867, 898, 437, 169, 478,260, 789, 216, 373, 820,
985, 943, 325)
Bonus.1 <- c(4, 2, 5, 0, 9, 6, 6, 7, 5, 8, 5, 5, 5, 8, 8, 8)
Bonus.2 <- c(4, 4, 0, 10, 5, 10, 2, 10, 8, 3, 9, 6, 3, 3, 2, 1)
Bonus.3 <- c(3, 0, 2, 7, 5, 0, 3, 6, 9, 5, 1, 2, 1, 5, 3, 2)
Bonus.4 <- c(1, 10, 2, 3, 2, 5, 7, 5, 3, 1, 6, 10, 3, 4, 7, 9)
data_file <- data.frame(Name, Initial.Value, Bonus.1, Bonus.2, Bonus.3, Bonus.4)
Rows <- unique(data_file$Name)
Output_file <- data.frame(matrix(0, ncol = length(Rows), nrow = 5))
colnames(Output_file) <- Rows
rownames(Output_file) <- colnames(data_file)[c(2,3,4,5,6)]
for(i in length(Rows)){ # Looks at each name in turn
Indices_Of_Interest <- which(lapply(data_file$Name,
function(x) any(match(x, Rows[i]))) == TRUE)
for(k in length(Output_file[, 1])){ # Goes down the Output_File
row_header <- rownames(Output_file)[k]
col_header <- Rows[i]
Output_file[row_header, col_header] <- sum(data_file[row_header][Indices_Of_Interest, ])
}
}
当我 运行 这段代码逐行工作时,它通过依次更新每个单元格来工作,但是当我 运行 for 循环时,它似乎只作用于最后一个单元格,留下另一个单元格为零,如下:
Output file, correct structure but not filled correctly
我不确定“Initial.Value”从何而来,但这为您提供了其他一切。
library(tidyverse)
data_file %>%
group_by(Name) %>%
summarise(across(starts_with("Bonus"), sum), .groups="drop") %>%
pivot_longer(names_to="Index", cols=starts_with("Bonus")) %>%
pivot_wider(values_from="value", names_from="Name")
# A tibble: 4 x 5
Index `Name 1` `Name 2` `Name 3` `Name 4`
<chr> <dbl> <dbl> <dbl> <dbl>
1 Bonus.1 20 19 28 24
2 Bonus.2 23 22 29 6
3 Bonus.3 17 9 18 10
4 Bonus.4 18 17 23 20
回应OP的评论:恕我直言,我认为他们所说的“复杂性”大部分是由于他们的数据格式不“整洁”造成的。 (请参阅我之前的评论和 link。)使用整洁的数据,大部分的复杂性都会消失。我声称 OP 的数据不整洁的原因是列名称中有相关信息:付款类型(“Initial.Value”与“奖金”)和奖金索引。这使生活变得比需要的更加困难。所以,这是一个可能的解决方案,从 OP 的修订测试数据(包括 Initial.Payment
)开始,基于一个可能整洁的数据集。
# Make the data tidy
tidyData <- data_file %>%
pivot_longer(
cols=c(starts_with("Bonus"), "Initial.Value"),
values_to="Value",
names_to="Source")
tidyData %>% head(5)
# A tibble: 5 x 3
Name Source Value
<fct> <chr> <dbl>
1 Name 1 Bonus.1 4
2 Name 1 Bonus.2 4
3 Name 1 Bonus.3 3
4 Name 1 Bonus.4 1
5 Name 1 Initial.Value 142
为什么我说这个格式比原来的格式好?仅仅是因为它使 后面的代码完全独立于奖金数量、支付类型(“Initial.Value”、“Bonus.x”、“其他支付类型”等)等)和不同名称的数量。我相信它在 OP 示例数据的上下文中是整洁的,但不一定在每个上下文中都是整洁的。例如,将 Source
分成两列或更多列可能很有用,例如 PaymentType
和 Index
。 'PaymentTypecould countain
Initial.Paymentor
Bonusand
Indexcould define the
Bonussuffix (and
0,
1or
NAfor
Initial.Payment` 条记录)。例如,这将允许轻松计算总体奖金(同样,独立于奖金类型的数量)。
所以,现在我有了一个整洁的数据集,整理所需的信息很简单:
totalBonus <- tidyData %>%
group_by(Name, Source) %>%
summarise(Value=sum(Value), .groups="drop")
这个数据集仍然很整洁,因此它是进一步操作的最佳选择,但不一定是展示的最佳选择。但这很容易修复。提供 OP 所需的输出:
totalBonus %>%
pivot_wider(names_from=Name, values_from=Value) %>%
arrange(desc(Source))
A tibble: 5 x 5
Source `Name 1` `Name 2` `Name 3` `Name 4`
<chr> <dbl> <dbl> <dbl> <dbl>
1 Initial.Value 3736 1084 2458 2253
2 Bonus.4 18 17 23 20
3 Bonus.3 17 9 18 10
4 Bonus.2 23 22 29 6
5 Bonus.1 20 19 28 24
代码中的错误是 for 循环中的语法。
for(i in length(Rows)){
将启动 for 循环以使用长度为 1 的整数,在上述情况下函数“length(Rows)”returns 为 4 的整数(值 4,长度 1)。因此循环只有一次迭代,因此只填充输出中的最后一个单元格 table.
循环应该按如下方式启动:
for(i in seq(length(Rows)){
嵌套循环也是如此,应该是:
for(k in seq(length(Output_file[, 1]))){