Вложенные циклы For для транспонирования и упорядочения таблицы данных с дублирующимися входами - PullRequest
0 голосов
/ 20 июня 2020

Не знаю, как кратко сформулировать эту проблему, так что название может быть плохим, и ответ может быть уже там. По сути, у меня проблема с вложенным l oop для вставки значений в новый фрейм данных.

У меня есть большой файл данных с повторяющимися именами и соответствующими инвестициями и бонусами, которые необходимо переупорядочить для создания новой таблицы / dataframe с итогами для каждого из связанных столбцов. Вот простой пример таблицы данных, которая у меня есть:

Тестовые данные

Я написал следующий код с тестовыми данными, файл CSV, написанный в Excel, и сохранено как Book1:

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 l oop, кажется, что он действует только на последнюю ячейку, оставив остальные ячейки равными нулю, как показано ниже:

Выходной файл, правильная структура, но неправильно заполнено

Ответы [ 2 ]

1 голос
/ 20 июня 2020

Я не уверен, откуда взялось "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: с уважением, я считаю, что большинство " сложность », на которую они ссылаются, вызвана тем, что их формат данных не является« аккуратным ». (См. Мой предыдущий комментарий и ссылку.) При работе с аккуратными данными большая часть сложности исчезнет. Причина, по которой я утверждаю, что данные OP не аккуратны, заключается в том, что в именах столбцов есть соответствующая информация: тип платежа («Начальное.значение» против «Бонуса») и индекс бонуса. Это делает жизнь труднее, чем нужно. Итак, вот возможное решение, начиная с пересмотренных тестовых данных 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

Почему я утверждаю, что этот формат лучше оригинала? Просто потому, что он делает код, следующий за , полностью независимым от количества бонусов, типов оплаты («Начальное.значение», «Bonus.x», «Другой тип платежа» и т. Д. c и т. Д. c) и количество разных имен . Я считаю, что это аккуратно в контексте данных примера OP, но не обязательно аккуратно в каждом контексте. Например, может быть полезно разделить Source на два или более столбца, например, PaymentType и Index. Записи 'PaymentType could countain Initial.Payment or Bonus and Index could define the Bonus suffix (and 0 , 1 or NA for 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
0 голосов
/ 20 июня 2020

Ошибка в коде была синтаксисом для l oop.

for(i in length(Rows)){

Будет инициировать для l oop работу с целым числом длины 1, а в приведенном выше случае функция length (Rows) возвращает целое число 4 (значение 4, длина 1). Следовательно, l oop имеет только одну итерацию и заполняет только последнюю ячейку в выходной таблице.

l oop должно было быть инициировано следующим образом:

for(i in seq(length(Rows)){

То же самое и для вложенного l oop, должно было быть:

for(k in seq(length(Output_file[, 1]))){
...