Использование цикла for для изменения столбца в кадре данных - PullRequest
3 голосов
/ 29 марта 2019

Я начинающий пользователь R, и я написал код, который, я считаю, может быть сокращен с помощью цикла for. Проблема в том, что я не могу понять, как написать цикл.

У меня есть фрейм данных со столбцом «TestGrade» со значениями, такими как «1 класс» или «Детский сад». Я пытаюсь изменить этот столбец, чтобы быть только числовым значением. Например, «Детский сад» будет изменен на 0, а «Класс 1» будет изменен на 1. Я предоставлю код ниже примера кадра данных, а также, как я решил проблему без цикла.

Любое руководство будет с благодарностью!

##Sample Data
FirstInitial <- c("A", "D", "M", "C", "J", "S", "K", "L", "M", "K", "G", "B", "F")
LastInitial <- c("S", "M", "T", "M", "A", "B", "H", "M", "S", "W", "L", "Z", "P")
TestGrade <- c('Kindergarten', 'Grade 1','Grade 2', 'Grade 3','Grade 4', 'Grade 5', 'Grade 6','Grade 7','Grade 8', 'Grade 9', 'Grade 10', 'Grade 11','Grade 12')

df <- data.frame(FirstInitial, LastInitial, TestGrade)

##The codes current function
if(any(df$TestGrade == 'Kindergarten')){
  df$TestGrade <- gsub('Kindergarten', '0', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 1')){
  df$TestGrade <- gsub('Grade 1', '1', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 2')){
  df$TestGrade <- gsub('Grade 2', '2', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 3')){
  df$TestGrade <- gsub('Grade 3', '3', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 4')){
  df$TestGrade <- gsub('Grade 4', '4', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 5')){
  df$TestGrade <- gsub('Grade 5', '5', df$TestGrade)
}

if(any(df$TestGrade == 'Grade 6')){
  df$TestGrade <- gsub('Grade 6', '6', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 7')){
  df$TestGrade <- gsub('Grade 7', '7', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 8')){
  df$TestGrade <- gsub('Grade 8', '8', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 9')){
  df$TestGrade <- gsub('Grade 9', '9', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 10')){
  df$TestGrade <- gsub('Grade 10', '10', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 11')){
  df$TestGrade <- gsub('Grade 11', '11', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 12')){
  df$TestGrade <- gsub('Grade 12', '12', df$TestGrade)
}

Ответы [ 6 ]

7 голосов
/ 29 марта 2019

Мы можем использовать ifelse, присвоить 0 для «Детского сада» и удалить «Оценка» из других

as.numeric(ifelse(df$TestGrade == "Kindergarten", 0, 
          sub("Grade ", "", df$TestGrade)))

#[1]  0  1  2  3  4  5  6  7  8  9 10 11 12
5 голосов
/ 29 марта 2019

Мы можем использовать case_when

library(dplyr)
library(readr)
df %>%
  mutate(TestGrade = case_when(as.character(TestGrade) == "Kindergarten"~ 0,
                               TRUE ~ parse_number(TestGrade)))

#   FirstInitial LastInitial TestGrade
#1             A           S         0
#2             D           M         1
#3             M           T         2
#4             C           M         3
#5             J           A         4
#6             S           B         5
#7             K           H         6
#8             L           M         7
#9             M           S         8
#10            K           W         9
#11            G           L        10
#12            B           Z        11
#13            F           P        12
4 голосов
/ 29 марта 2019

Первое сокращение: вам не нужно if(any(...)). gsub умный, это как найти / заменить. Команда gsub('Grade 9', '9', df$TestGrade) заменит 'Grade 9' на '9' и больше ничего не трогает. Таким образом, удалив все ваши if заявления, мы получим:

df$TestGrade <- gsub('Kindergarten', '0', df$TestGrade)
df$TestGrade <- gsub('Grade 1', '1', df$TestGrade)
df$TestGrade <- gsub('Grade 2', '2', df$TestGrade)
df$TestGrade <- gsub('Grade 3', '3', df$TestGrade)
df$TestGrade <- gsub('Grade 4', '4', df$TestGrade)
df$TestGrade <- gsub('Grade 5', '5', df$TestGrade)
df$TestGrade <- gsub('Grade 6', '6', df$TestGrade)
df$TestGrade <- gsub('Grade 7', '7', df$TestGrade)
df$TestGrade <- gsub('Grade 8', '8', df$TestGrade)
df$TestGrade <- gsub('Grade 9', '9', df$TestGrade)
df$TestGrade <- gsub('Grade 10', '10', df$TestGrade)
df$TestGrade <- gsub('Grade 11', '11', df$TestGrade)
df$TestGrade <- gsub('Grade 12', '12', df$TestGrade)

Следующее улучшение, мы могли бы сделать цикл. Это в точности эквивалентно приведенному выше коду, просто меньше печатать.

pattern = c("Kindergarten", paste("Grade", 1:12))
replacement = as.character(0:12)

for (i in seq_along(pattern)) {
  df$TestGrade <- gsub(pattern[i], replacement[i], df$TestGrade)
}

Еще лучше, мы могли бы быть умнее, сделать детский сад особым случаем и просто удалить "Grade " из всего остального, как в ответах Джуана и Ронака. Другой вариант этого таков:

df$TestGrade = as.character(df$TestGrade) # needed only if it is a factor
df$TestGrade[df$TestGrade == "Kindergarten"] = 0
df$TestGrade = sub("Grade ", "", df$TestGrade)
df$TestGrade = as.numeric(df$TestGrade) # if needed

Если мы действительно хотим быть модными, мы можем установить fixed = TRUE внутри sub(). Это говорит sub, что мы хотим только точные совпадения, мы не пытаемся использовать регулярные выражения. Это заставит код работать быстрее, но если у вас много данных, вы не заметите разницы. Если у вас более 100 000 строк, этот метод будет довольно быстрым:

# optimized
df$TestGrade = as.character(df$TestGrade) # needed only if it is a factor
df$TestGrade[df$TestGrade == "Kindergarten"] = 0
df$TestGrade = as.integer(sub("Grade ", "", df$TestGrade, fixed = TRUE))
3 голосов
/ 29 марта 2019

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

key <- c('Kindergarten',
         'Grade 1',
         'Grade 2',
         'Grade 3',
         'Grade 4',
         'Grade 5',
         'Grade 6',
         'Grade 7',
         'Grade 8',
         'Grade 9',
         'Grade 10',
         'Grade 11',
         'Grade 12')
dat <- c('Grade 3', 'Grade 5', 'Grade 2')
dat <- factor(dat, levels = key)
dat <- as.numeric(dat) - 1
dat

Мы вычитаем 1 в конце, потому что факторы начинаются с 1, и вы хотели, чтобы детский сад был установлен на 0.

3 голосов
/ 29 марта 2019

Это можно сделать без использования цикла for с использованием двухстрочного кода. Я также предлагаю добавить stringsAsFactors = F в команду data.frame перед запуском этих строк

df$TestGrade[df$TestGrade == "Kindergarten"] = 0
df$TestGrade <- gsub("Grade ", "", df$TestGrade)

> df
   FirstInitial LastInitial TestGrade
1             A           S         0
2             D           M         1
3             M           T         2
4             C           M         3
5             J           A         4
6             S           B         5
7             K           H         6
8             L           M         7
9             M           S         8
10            K           W         9
11            G           L        10
12            B           Z        11
13            F           P        12
2 голосов
/ 29 марта 2019

Это решает вашу проблему здесь:

df$TestGrade <- sapply(df$TestGrade,function(el)
  {
  if(el == "Kindergarten") return(0)
  else return(as.numeric(sub("Grade ","",el)))
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...