Колонна манипуляции R - PullRequest
       8

Колонна манипуляции R

0 голосов
/ 03 июля 2018

Я довольно новичок в работе с R, и я надеялся, что кто-то может указать мне правильный путь. У меня есть набор данных в виде таблицы, и мне нужно пройти через каждую строку. В каждой строке я должен проверить столбцы в наборах по 3. т.е. если значение одного из столбцов = 0, мне нужно удалить все три столбца и оценить следующие 3 столбца.

data_set <- as.data.frame(matrix(nrow=2))
data_set$Basket1<- c(45,35)
data_set$Type1 <- c("Normal","Premium")
data_set$Amount1 <- c(4,5)

data_set$Basket2 <- c(4,98)
data_set$Type2 <- c("Normal","Normal")
data_set$Amount2 <- c(0,4)

#when Type is "Premium" I want to remove the values for 
#Basket1,Type1,Amount1 
#and shift the next 3 cells to the left

Ответы [ 4 ]

0 голосов
/ 03 июля 2018

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

library(tidyverse)

data_set %>%

  # add a unique identifier for the original shape:
  mutate(customer = 1:n()) %>%

  # turn into a long thin format:
  gather(variable, value, -customer) %>%

  # remove any NA values from the unbalanced table (and the empty column at the beginning of the original)
  filter(!is.na(value)) %>%

  # extract the numbers from the original column names:
  mutate(column_set = as.numeric(str_extract(variable, "[0-9]+")),
         variable = gsub("[0-9]+", "", variable)) %>%

  # limit the data to the first "normal" column set for each customer
  group_by(customer) %>%
  mutate(best_column_set = min(column_set[value == "Normal"])) %>%
  filter(column_set == best_column_set) %>%

  # drop the columns we don't need and return to wide format:
  select(-column_set, -best_column_set) %>%
  spread(variable, value) %>%

  # convert from characters back to numbers
  mutate(Basket = as.numeric(Basket),
         Amount = as.numeric(Amount))

Возвращает:

# A tibble: 2 x 4
# Groups:   customer [2]
  customer Amount Basket Type  
     <int>  <dbl>  <dbl> <chr> 
1        1      4     45 Normal
2        2      4     98 Normal

Этот метод зависит от того, насколько они имеют некоторую структуру в том, как маркируются исходные столбцы; например, что они пронумерованы набором столбцов, и что в именах столбцов нет других чисел.

0 голосов
/ 03 июля 2018

Я бы настоятельно рекомендовал не думать о смещении значений влево или вправо - он не использует большинство объектов фрейма данных R, где столбцы должны рассматриваться как имеющие целостность. Поэтому вместо того, чтобы сдвигать и удалять ячейки, я думаю, что вы должны добавить новые столбцы справа на основе необходимой логики, а затем (при необходимости) удалить все исходные столбцы. Вот один из способов сделать это, создав новые столбцы amount_n, basket_n и type_n и отбросив остальные в конце:

library(dplyr)
data_set <- data_set %>%
  mutate(
    basket_n = case_when(
      # If Type1 is Normal we use its basket:
      Type1 == "Normal" ~ Basket1,
      # If not, then see if Type2 is normal and we can use that (and so on):
      Type2 == "Normal" ~ Basket2
    ),
    amount_n = case_when(
      Type1 == "Normal" ~ Amount1,
      Type2 == "Normal" ~ Amount2
    ),
    type_n = "Normal"
  ) %>%
  select(type_n, basket_n, amount_n)
0 голосов
/ 03 июля 2018
data=read.table(text="   Basket1 Type1   Amount1 Basket2 Type2   Amount2 Basket3 Type3   Amount3
1   Normal  1   10  Normal  3   12  Premium 0
2   Normal  0   3   Normal  3   45  Premium 3
3   Normal  1   3   Normal  3   3   Premium 67
4   Normal  1   5   Normal  3   54  Premium 45",h=T)

чтобы сдвинуть влево, вы можете сделать:

data%>%
  rownames_to_column%>%
  reshape(matrix(2:ncol(.),3),idvar = 1,dir="long")%>%
  filter(!rowSums(.==0)>0)%>%
  group_by(rowname)%>%
  mutate(time=1:n())%>%
  arrange(time,rowname)%>%
  data.frame()%>%
  reshape(timevar = "time",idvar = "rowname",dir="wide")%>%
  select(-rowname)%>%
      rename_all(~sub("\\d.","",.x))

 Basket1  Type1 Amount1 Basket2   Type2 Amount2 Basket3   Type3 Amount3
1       1 Normal       1      10  Normal       3      NA    <NA>      NA
2       3 Normal       3      45 Premium       3      NA    <NA>      NA
3       3 Normal       1       3  Normal       3       3 Premium      67
4       4 Normal       1       5  Normal       3      54 Premium      45

РЕДАКТИРОВАТЬ ::

Для данных, которые вы предоставили позже:

data_set[-1]%>%
   rownames_to_column%>%
   reshape(matrix(2:ncol(.),3),idvar = 1,dir="long")%>%
   filter(!rowSums(.==0)>0)%>%
   group_by(rowname)%>%mutate(time=1:n())%>%
   arrange(time,rowname)%>%
   data.frame()%>%
   reshape(timevar = "time",idvar = "rowname",dir="wide")%>%
   select(-rowname)%>%
   rename_all(~sub("\\d.","",.x))

  Basket1   Type1 Amount1 Basket2  Type2 Amount2
1      45  Normal       4      NA   <NA>      NA
2      35 Premium       5      98 Normal       4
0 голосов
/ 03 июля 2018

Пожалуйста, посмотрите мое обновление в конце на основе нового примера, который вы добавили.

#I have a data set in the form of a tibble
data_set              <- as.data.frame(matrix(nrow=8))
data_set$column1_set1 <- c(1,1,1,1,1,1,1,1)
data_set$column2_set1 <- c(1,1,1,1,0,1,1,1)
data_set$column3_set1 <- c(1,1,1,1,1,1,1,1)

data_set$column1_set2 <- c(1,1,1,1,1,1,1,1)
data_set$column2_set2 <- c(1,1,1,1,1,1,1,1)
data_set$column3_set2 <- c(1,1,1,1,1,1,1,1)
data_set$V1           <- NULL

data_set <- as.tibble(data_set)

# In each row I have to check columns in sets of 3. 
#   i.e if one of the columns value=0 I have to delete all three columns 
#   and evaluate the next 3 columns. 

Вы можете сделать это так:

cn               <- colnames(data_set)

for(i in seq(1,length(cn),3)){
  if(any(colSums(data_set[,i:(i+2)]) < nrow(data_set))){
    data_set <- data_set[,!colnames(data_set) %in% cn[i:(i+2)]]

  } else{
    next
  }
}

В новом примере у нас есть несколько нечисловых столбцов. Единственное изменение, которое мы должны сделать, это сначала проверить, являются ли они числовыми.

cn               <- colnames(data_set)

for(i in seq(1,length(cn),3)){

    cn_tmp   <- cn[i:(i+2)]
    cn_tmp   <- ifelse(class(data_set[,colnames(data_set) %in% cn_tmp])=="numeric",
                       cn_tmp, cn_tmp[!cn_tmp==cn_tmp[i]])
    cn_tmp   <- ifelse(class(data_set[,colnames(data_set) %in% cn_tmp])=="numeric",
                       cn_tmp, cn_tmp[!cn_tmp==cn_tmp[i+1]])
    cn_tmp   <- ifelse(class(data_set[,colnames(data_set) %in% cn_tmp])=="numeric",
                       cn_tmp, cn_tmp[!cn_tmp==cn_tmp[i+2]])

  if(any(colSums(data_set[,colnames(data_set) %in% cn_tmp]) < nrow(data_set))){
    data_set <- data_set[,!colnames(data_set) %in% cn[i:(i+2)]]

  } else{
    next
  }
}
...