Как извлечь имена переменных и переменные данные из одних и тех же строк, чтобы создать фрейм данных?

#r #regex #text #dataframe

#r #регулярное выражение #текст #фрейм данных

Вопрос:

У меня есть серия текстовых файлов, которые выглядят следующим образом:

 USABILITY DATA
Level Name: LVL_Introduction
    Time Spent: 323.233307
    Times Teleported: 6
    NumDeaths: 0

Level Name: LVL_1-1
    Time Spent: 36.760727
    Times Teleported: 1
    NumDeaths: 0

Level Name: LVL_1-2
    Time Spent: 45.953598
    Times Teleported: 1
    NumDeaths: 0

Level Name: LVL_1-3
    Time Spent: 176.440704
    Times Teleported: 0
    NumDeaths: 0

Level Name: LVL_1-4
    Time Spent: 281.797485
    Times Teleported: 0
    NumDeaths: 0
  

Каждый текстовый файл представляет данные одного игрока из игрового сеанса в головоломке от уровня к уровню. Некоторые игроки достигают разных уровней, чем другие.

Я хотел бы преобразовать этот текстовый файл в фрейм данных, который дал бы мне таблицу с именем уровня (например: «LVL_1-3») в качестве заголовка столбца и «Затраченное время», «Время телепортирования» и «NumDeaths» в качестве заголовков строк.

                     LVL_Introduction    Lvl_1-1    etc...
Time Spent:         323.233307          36.760727
Times Teleported:   6                   1 
NumDeath:           0                   0
  

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

Спасибо, Митчелл

Комментарии:

1. Совпадают ли значения «Имя уровня» во всех текстовых файлах? Кроме того, вы уверены, что именно так вы хотите структурировать выходной фрейм данных? Кажется, что было бы более уместно иметь столбцы что-то вроде c("Player","Level","Time.Spent","Times.Teleported","NumDeath")

2. Существует основной набор возможных значений, но значения могут отличаться от текстового файла к текстовому файлу, как и количество записей уровня. Уровни могут быть (от LVL_1-1 до LVL_1-5) вплоть до (от LVL_4-1 до LVL_4-5), потому что существует 4 набора по 5 уровней. Существует вводный уровень и главное меню. Разные игроки достигают разных точек в игре, поэтому каждый файл отличается.

3. @rosscova Я думал, что мог бы расплавить этот фрейм данных, чтобы у меня был каждый пользователь в виде отдельного столбца, но для этого потребовались бы имена строк, подобные этим: Time_Intro Teleports_Intro Deaths_Intro Time_1-1 Teleports_1-1 Deaths_1-1 Time_1-2 Teleports_1-2 Deaths_1-2 Time_1-3 Teleports_1-3 Deaths_1-3 Time_1-4 Teleports_1-4 Deaths_1-4 Time_1-5 Teleports_1-5 Deaths_1-5 Time_Menu Teleports_Menu Deaths_Menu Time_2-1 Teleports_2-1 Deaths_2-1

4. Надеюсь, это имеет смысл — есть 17 возможных уровней игры, 3 переменные, которые я измеряю, и много разных пользователей. Я могу использовать переменные x уровней или «переменную по уровню» x пользователя. Было бы проще вручную ввести данные в чистый файл .csv, я просто хотел посмотреть, существует ли решение R.

Ответ №1:

Вот мое предложение, хотя оно не совсем соответствует тому, что вы просили, я думаю, вы обнаружите, что оно предоставляет вам ваши данные в очень аккуратном (читай: полезном) виде:

 library( readr ) 
library( plyr )

import <- function( datafile.name ) {

    new <- read_delim( datafile.name,
                       col_names = c( "category", "data" ), 
                       delim = ":",
                       trim_ws = TRUE )
    new <- new[ !is.na( new$data ), ]

    output <- data.frame(
        new[ new$category == "Level Name", "data" ],
        new[ new$category == "Time Spent", "data" ],
        new[ new$category == "Times Teleported", "data" ],
        new[ new$category == "NumDeaths", "data" ],
        stringsAsFactors = FALSE
    )
    names( output ) <- c( "level.name", "time.spent", "times.teleported", "num.deaths" )

    # get the username from the file name
    output$user <- datafile.name
    return( output )
}

# get a list of files
setwd( [where your files are located] )
filelist <- list.files()

# and apply the function above to all those files to create one big dataframe
df <- ldply( .data = filelist,
             .fun = import )
  

Примечание, я бы предположил, что ваши имена файлов напрямую не представляют имена пользователей, поэтому вам нужно будет выполнить преобразование. Достаточно чего-то настолько простого, насколько gsub(".txt","",datafile.name) это возможно.

Если требуется импортировать много файлов, вы также можете захотеть выполнить этот процесс в нескольких потоках. Здесь он находится на 4 ядрах:

 library( doMC )
registerDoMC( cores = 4 )
df <- ldply( .data = filelist,
             .fun = import,
             .parallel = TRUE )
  

Комментарии:

1. Спасибо за предложения! Я попробую их 🙂

Ответ №2:

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

Здесь я создаю функцию для обработки фрагментов — она используется read.delim для чтения variable: value данных, а затем транспонирует их с помощью tidyr::spread

 library(tidyr)
library(readr)

f <- function(x, pos) {
  dat <- read.delim(text = x, sep = ":", header = FALSE, stringsAsFactors = FALSE)
  return(spread(dat, V1, V2))
}
  

чтение фрагментов с DataFrameCallback помощью дает data.frame результаты через rbind

 rawData <- read_lines_chunked(file = "gamedata.txt",
                              skip = 1,
                              chunk_size = 5, 
                              callback = DataFrameCallback$new(f))
  

тогда остается только переформатировать это в соответствии с тем, что вы просили (использование 4 для идентификатора столбца ненадежно, но это чистый пример)

 gameData <- setNames(data.frame(t(rawData[, -4])), rawData[, 4])[c(2,3,1), ]
gameData
#>                       LVL_Introduction    LVL_1-1    LVL_1-2     LVL_1-3
#>     Time Spent              323.233307  36.760727  45.953598  176.440704
#>     Times Teleported                 6          1          1           0
#>     NumDeaths                        0          0          0           0
#>                          LVL_1-4
#>     Time Spent        281.797485
#>     Times Teleported           0
#>     NumDeaths                  0