2017-01-17 19 views
1

Фон

Целью является чтение из файла CSV и запись содержимого в формате таблицы Markdown.Преобразование содержимого файла CSV в Markdown

Приложение использует R двигатель Renjin, который не поддерживает knitr, kable или pandoc.

Проблема

Команда write.table не имеет eol вариант, но не соответствующий sol вариант. Таким образом, для следующих целей:

f <- read.csv('planning.csv') 
write.table(
    format(f, digits=2), "", 
    sep="|", row.names=F, col.names=F, quote=F, eol="|\n") 

Выход выглядит следующим образом:

Geothermal|1250.0|Electricity|0.0| 
Houses| 13.7|Shelter|4.2| 
Compostor| 1.2|Recycling|0.2| 

Но каждая строка должна появиться с | префиксом следующим образом:

|Geothermal|1250.0|Electricity|0.0| 
|Houses| 13.7|Shelter|4.2| 
|Compostor| 1.2|Recycling|0.2| 

Это должно быть возможным сделайте что-нибудь вроде (обратите внимание на дополнительную трубку eol):

write.table(
     format(f, digits=2), "", 
     sep="|", row.names=F, col.names=F, quote=F, eol="|\n|") 

Затем захватите все как строку, соедините ведущую трубу и, наконец, обрезайте постороннюю концевую трубу. То есть, устранить проблемы с выходом, который будет выглядеть следующим образом:

Geothermal|1250.0|Electricity|0.0| 
|Houses| 13.7|Shelter|4.2| 
|Compostor| 1.2|Recycling|0.2| 
|Fire Station| -9.6|Protection|0.5| 
|Roads| 0.0|Transport|0.9| 
| 

Такая обработка строк не кажется очень R-как, хотя.

Вопрос

Что является наиболее эффективным способом превращать CSV файл в формате Markdown, не полагаясь на сторонние библиотеки?

Markdown вкус в вопросе выглядит следующим образом:

|Header|Header|Header| 
|---|---|---| 
|Data|Data|Data| 
|Data|Data|Data| 

подсказки для того, как писать только данные заголовка и разделитель заголовка таблицы, также приветствуется.

+2

Существует [существенное обсуждение] (https://groups.google.com/forum/?nomobile=true#!searchin/pandoc-discuss/csv%7Csort:relevance/pandoc-discuss/znGQ62WpWrg/lFpgTPZzGgAJ) о таблицах CSV в группе pandoc google, включая фильтр pandoc. Я не знаю его состояния, но вы можете это проверить. – r2evans

+1

apply(), lapply(), sapply(), mapply(), vapply() - все функции из базового пакета, который всегда доступен изнутри Renjin. – akbertram

ответ

3

Поскольку вы хотите поместить его в уценку, я думаю, можно с уверенностью сказать, что размер таблицы управляемый, поэтому производительность не является фактором. (Edit # 3: У меня были некоторые мелкие ошибки, связанные с наличием имен строк, так чтобы упростить вещи, я буду полностью удалить их из выборки данных.)

mtcars$rowname <- rownames(mtcars) 
rownames(mtcars) <- NULL 
mtcars <- mtcars[,c(ncol(mtcars), 1:(ncol(mtcars)-1))] 
head(mtcars) 
#    rowname mpg cyl disp hp drat wt qsec vs am gear carb 
# 1   Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 
# 2  Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 
# 3  Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 
# 4 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 
# 5 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 
# 6   Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 

Теперь работа:

dashes <- paste(rep("---", ncol(mtcars)), collapse = "|") 
txt <- capture.output(
    write.table(mtcars, stdout(), quote = FALSE, sep = "|", row.names = FALSE) 
) 
txt2 <- sprintf("|%s|", c(txt[1], dashes, txt[-1])) 
head(txt2) 
# [1] "|rowname|mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb|" 
# [2] "|---|---|---|---|---|---|---|---|---|---|---|---|"  
# [3] "|Mazda RX4|21|6|160|110|3.9|2.62|16.46|0|1|4|4|"   
# [4] "|Mazda RX4 Wag|21|6|160|110|3.9|2.875|17.02|0|1|4|4|"  
# [5] "|Datsun 710|22.8|4|108|93|3.85|2.32|18.61|1|1|4|1|"  
# [6] "|Hornet 4 Drive|21.4|6|258|110|3.08|3.215|19.44|1|0|3|1|" 

Если вас беспокоит выравнивание, вы можете проверить на character (и, возможно, другие, к вам). При этом используется выравнивание строки формата уценки таблицы:

(ischar <- vapply(mtcars, is.character, logical(1))) 
# rowname  mpg  cyl disp  hp drat  wt qsec  vs  am gear carb 
# TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 
dashes <- paste(ifelse(ischar, ":--", "--:"), collapse = "|") 
txt <- capture.output(write.table(mtcars, stdout(), quote = FALSE, sep = "|", row.names = FALSE)) 
txt2 <- sprintf("|%s|", c(txt[1], dashes, txt[-1])) 
head(txt2) 
# [1] "|rowname|mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb|" 
# [2] "|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|"  
# [3] "|Mazda RX4|21|6|160|110|3.9|2.62|16.46|0|1|4|4|"   
# [4] "|Mazda RX4 Wag|21|6|160|110|3.9|2.875|17.02|0|1|4|4|"  
# [5] "|Datsun 710|22.8|4|108|93|3.85|2.32|18.61|1|1|4|1|"  
# [6] "|Hornet 4 Drive|21.4|6|258|110|3.08|3.215|19.44|1|0|3|1|" 

И когда вы, наконец, готовы сохранить, использовать cat(txt2, file = "sometable.md") (или writeLines).

Редактировать # 1: обратите внимание, что другие предлагаемые ответы (включая шахты выше) не обращаются символов трубы в пределах содержания:

mtcars$mpg[1] <- "2|1.0" 
ischar <- vapply(mtcars, is.character, logical(1)) 
dashes <- paste(ifelse(ischar, ":--", "--:"), collapse = "|") 
txt <- capture.output(write.table(mtcars, stdout(), quote = FALSE, sep = "|", row.names = FALSE)) 
txt2 <- sprintf("|%s|", c(txt[1], dashes, txt[-1])) 
head(txt2, n = 3) 
# [1] "|rowname|mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb|" 
# [2] "|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|"  
# [3] "|Mazda RX4|2|1.0|6|160|110|3.9|2.62|16.46|0|1|4|4|"  
###    ^this is the problem 

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

ischar <- vapply(mtcars, is.character, logical(1)) 
mtcars[ischar] <- lapply(mtcars[ischar], function(x) gsub("\\|", "&#124;", x)) 
dashes <- paste(ifelse(ischar, ":--", "--:"), collapse = "|") 
txt <- capture.output(write.table(mtcars, stdout(), quote = FALSE, sep = "|", row.names = FALSE)) 
txt2 <- sprintf("|%s|", c(txt[1], dashes, txt[-1])) 
head(txt2, n = 3) 
# [1] "|rowname|mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb|" 
# [2] "|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|"  
# [3] "|Mazda RX4|2&#124;1.0|6|160|110|3.9|2.62|16.46|0|1|4|4|" 
###    ^^^^^^ this is the pipe, interpreted correctly in markdown 

Это не очень хорошо работает, когда труба находится в пределах блока кода, хотя обходной путь был предложен здесь: https://stackoverflow.com/a/17320389/3358272

На этом этапе, как предложил @alistaire, вы несколько переопределяете knitr::kable. Если на то пошло, просто возьмите knitr/R/table.R) и используйте kable_markdown, что делает выпадение трубы для вас. Требуется character matrix, а не data.frame, поэтому kable_markdown(as.matrix(mtcars)). Вы не можете просто захватить одну функцию, поскольку она использует несколько вспомогательных функций также в этом файле. Вы можете, конечно, сократить некоторые функции, в том числе kable, который требует функций в других файлах.

Edit # 2: так как вы сказали, renjin не поддерживает *apply функции (комментарий предполагает, что это неправильно, но я буду продолжать ради аргумента), вот for -loop реализация, которая включает в себя выравнивание и | -escaping:

mtcars$mpg[1] <- "2|1.0" # just a reminder that it's here 
dashes <- rep("--:", length(mtcars)) 
for (i in seq_along(mtcars)) { 
    if (is.character(mtcars[[i]]) || is.factor(mtcars[[i]])) { 
    mtcars[[i]] <- gsub("\\|", "&#124;", mtcars[[i]]) 
    dashes[i] <- ":--" 
    } 
} 
txt <- capture.output(write.table(mtcars, stdout(), quote = FALSE, sep = "|", row.names = FALSE)) 
txt2 <- sprintf("|%s|", c(txt[1], paste(dashes, collapse = "|"), txt[-1])) 
head(txt2, n = 3) 
# [1] "|rowname|mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb|" 
# [2] "|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|"  
# [3] "|Mazda RX4|2&#124;1.0|6|160|110|3.9|2.62|16.46|0|1|4|4|" 

Для записи, моя *apply и for -loop реализации эффективна такая же производительность, в то время как решение @ alistaire является более чем вдвое быстрее (с mtcars):

Unit: microseconds 
       expr  min  lq  mean median  uq  max neval 
    apply_noalign 917.881 947.9665 1031.9288 971.3060 1041.5050 1999.499 100 
     apply_align 945.960 975.1350 1083.2856 995.7390 1063.7500 3523.101 100 
apply_align_pipes 1110.429 1148.5360 1255.5460 1176.9815 1275.2600 1905.778 100 
      forloop 1188.104 1217.0950 1309.2549 1261.2205 1342.3600 2979.010 100 
     alistaire 451.830 473.7105 511.5778 496.1370 518.5645 827.443 100 
    alistaire_pipes 593.687 626.6900 718.6898 652.7645 700.5360 5460.970 100 

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

+0

Я изначально не видел ограничений на функции '* apply', извините за это. Я рад первым работам. (На самом деле довольно просто заменить 'apply' на петли 'for', если это необходимо.) – r2evans

+0

Ваше утверждение в ** Правке № 2 ** неверно: Renjin полностью поддерживает функции' * apply'. Ответ, предоставленный @alistaire, который использует функциональный 'Reduce()', также отлично работает в Renjin. Наконец, в наборе данных 'mtcars' нет столбцов символов, поэтому здесь вы можете использовать' ischar'. – mjkallen

+0

OP сказал это, я доверял этому и не подтвердил, мой плохой (я не использую renjin, так что не знаю).Я изменил один столбец на символ (3-й блок кода), поэтому 'ischar' * не * лишний в целом, хотя я вижу, что я пропустил ошибку во втором блоке, который бы сделал это более ясным. – r2evans

3

Вы можете написать свой отзыв о kable, если хотите; это в основном только paste.

x <- read.csv(system.file('misc', 'exDIF.csv', package = 'utils')) 

md_table <- function(df){ 
    paste0('|', paste(names(df), collapse = '|'), '|\n|', 
      paste(rep('---', length(df)), collapse = '|'), '|\n|', 
      paste(Reduce(function(x, y){paste(x, y, sep = '|')}, df), collapse = '|\n|'), '|') 
} 

cat(md_table(x)) 
#> |Var1|Var2| 
#> |---|---| 
#> |2.7|A| 
#> |3.14|B| 
#> |10|A| 
#> |-7|A| 

cat(md_table(head(mtcars))) 
#> |mpg|cyl|disp|hp|drat|wt|qsec|vs|am|gear|carb| 
#> |---|---|---|---|---|---|---|---|---|---|---| 
#> |21|6|160|110|3.9|2.62|16.46|0|1|4|4| 
#> |21|6|160|110|3.9|2.875|17.02|0|1|4|4| 
#> |22.8|4|108|93|3.85|2.32|18.61|1|1|4|1| 
#> |21.4|6|258|110|3.08|3.215|19.44|1|0|3|1| 
#> |18.7|8|360|175|3.15|3.44|17.02|0|0|3|2| 
#> |18.1|6|225|105|2.76|3.46|20.22|1|0|3|1| 

Перепишите вторую линию для обработки оснований в зависимости от типа, если вам нравится.