嵌套 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 分成两列或更多列可能很有用,例如 PaymentTypeIndex。 'PaymentTypecould countainInitial.PaymentorBonusandIndexcould define theBonussuffix (and0, 1orNAforInitial.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]))){