Получение 2-го элемента неравномерного списка - PullRequest
1 голос
/ 16 марта 2019

(вопрос о списках в R)

Я работаю с очень большим набором данных, где у меня есть столбец даты, который принимает одну из двух форм:

  • тип даты 1: «ММ / ДД / ГГГГ ЧЧ: ММ: СС AM»
  • тип даты 2: «ММ / ДД / ГГГГЧ: ММ: СС AM - ММ / ДД / ГГГГ ЧЧ: ММ: СС AM»

Мне нужно разделить этот столбец в зависимости от того, есть ли тире (тип 2), и поместить их в два столбца («Дата 1» и «Дата 2»). Если я столкнусь со строкой с датой типа 1, тогда дата просто займет «Дата 1», а «Дата 2» будет просто NA.

Вот что я ищу - конвертируйте что-то похожее на это:

c(
    rep("8/20/2018 9:18:45 AM", 15),
    rep("8/20/2018 9:18:45 AM - 8/12/2018 9:18:45 AM", 15)
  )

К этому:

data.frame(
  Date1 = c(rep("8/15/2018 9:18:45 AM", 15), rep("8/20/2018 9:18:45 AM", 15)),
  Date2 = c(rep(NA, 15), rep("8/12/2018 9:18:45 AM", 15))
)

# output
# Date1                Date2
# 1  8/15/2018 9:18:45 AM                 <NA>
#   2  8/15/2018 9:18:45 AM                 <NA>
#   3  8/15/2018 9:18:45 AM                 <NA>
#   4  8/15/2018 9:18:45 AM                 <NA>
#   5  8/15/2018 9:18:45 AM                 <NA>
#   6  8/15/2018 9:18:45 AM                 <NA>
#   7  8/15/2018 9:18:45 AM                 <NA>
#   8  8/15/2018 9:18:45 AM                 <NA>
#   9  8/15/2018 9:18:45 AM                 <NA>
#   10 8/15/2018 9:18:45 AM                 <NA>
#   11 8/15/2018 9:18:45 AM                 <NA>
#   12 8/15/2018 9:18:45 AM                 <NA>
#   13 8/15/2018 9:18:45 AM                 <NA>
#   14 8/15/2018 9:18:45 AM                 <NA>
#   15 8/15/2018 9:18:45 AM                 <NA>
#   16 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 17 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 18 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 19 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 20 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 21 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 22 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 23 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 24 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 25 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 26 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 27 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 28 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 29 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM
# 30 8/20/2018 9:18:45 AM 8/12/2018 9:18:45 AM

Я хочу, чтобы первый подэлемент списка занимал столбец Date1, а второй подэлемент (если он существует) занимал столбец Date2. Если второго элемента нет, я хочу, чтобы строка Date2 была NA.

Моя первая попытка - создать новый список, в котором я использую условие. Если длина подэлемента всего одна, я создаю второй подэлемент и устанавливаю его на NA.

dates = c(
  c(
    rep("8/20/2018 9:18:45 AM", 15),
    rep("8/20/2018 9:18:45 AM - 8/12/2018 9:18:45 AM", 15)
  )
)


# create the date split. Split the text based on the dash 
dates_split = strsplit(dates, " - ")
# note where the correct dates are. date_split[[15]] as one sub element and date_split[[16]] has two
dates_split[[15]];dates_split[[16]]

# so far so good






# create a conditional where if there is only one date (one sub element), set the second sub element to zero.
for(i in 1:length(dates_split)){
  if(length(dates_split[i]) == 1){
    dates_split[[i]][2] = NA
  } else {}
}

# the above loop does not behave as expected. The dates_split[[16]][2] is now gone (it turned to NA)






# create a vector for Date1 and Date2
Date1 = unlist(lapply(dates_split, "[[", 1))
Date2 = unlist(lapply(dates_split, "[[", 2))

# put each date type in their appropriate column
date_df = data.frame(
  Date1 = Date1,
  Date2 = Date2
)

# second column is all NA's. Where did the second sub elements go?

Мой предыдущий скрипт для меньшего набора данных сделал что-то вроде этого, чтобы обойти это:

dates = strsplit(dates, " - ")

# this takes forever to do. Is there a way to do this without using a loop??
for(i in 1:nrow(dates_split)){
  date_df$Date1 = dates[[i]][1]
  date_df$Date2 = dates[[i]][2]
}

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

Есть ли какой-нибудь совет, как изменить этот шаг, чтобы я создал NA для второго подэлемента без непреднамеренного превращения всего в NA?

# create a conditional where if there is only one date (one sub element), set the second sub element to zero.
for(i in 1:length(dates_split)){
  if(length(dates_split[i]) == 1){
    dates_split[[i]][2] = NA
  } else {}
}

# the above loop does not behave as expected. The dates_split[[16]][2] is now gone (it turned to NA)

Спасибо!

1 Ответ

1 голос
/ 17 марта 2019

Во-первых, ответить на следующее

Есть ли какой-нибудь совет, как изменить этот шаг, чтобы я создал NA для второго подэлемента без случайного поворота все в АН?

просто замените [i] на [[i]] во второй строке for loop.

Во-вторых, я сделал несколько изменений в вашем коде и проверил скорость. На 10 миллионов точек данных ушло около 15 секунд. Так что это довольно быстро. Я попытался заменить цикл for на lapply, но это не привело к увеличению скорости. Теперь вы можете ускорить его (возможно, значительно), используя пакет data.table, но для этого есть некоторая кривая обучения. Вот полный код для тестирования, чтобы увидеть, все ли работает так, как вы ожидаете.

# how many times to repeat dates (five million for testing)
rep.num = 5000000

# create dummy dates
dates = c(
    rep("8/20/2018 9:18:45 AM", rep.num),
    rep("8/20/2018 9:18:45 AM - 8/12/2018 9:18:45 AM", rep.num)
)

# create the date split. Split the text based on the dash 
# using fixed = T here results in significant speed increase
dates_split <- strsplit(dates, " - ", fixed = T)

# note where the correct dates are. date_split[[rep.num]] as one sub element and date_split[[rep.num + 1]] has two
dates_split[[rep.num]]
dates_split[[rep.num + 1]]
dates_split[[rep.num + 1]][1]
dates_split[[rep.num + 1]][2]

# create a conditional where if there is only one date (one sub element), set the second sub element to zero.
for(i in 1:length(dates_split)){
  if(length(dates_split[[i]]) == 1){
    dates_split[[i]][2] = NA
  }
}

# put each date type in their appropriate column
date_df = data.frame(
  Date1 = sapply(dates_split, "[[", 1),
  Date2 = sapply(dates_split, "[[", 2)
)
...