Rに読み込む必要がある多くの固定幅ファイル(つまり、分離文字がない)を使用しています。したがって、通常、文字列を変数に解析するための列幅の定義があります。 _read.fwf
_を使用して、問題なくデータを読み取ることができます。ただし、大きなファイルの場合、これにはlong時間がかかる場合があります。最近のデータセットの場合、約500,000行と143変数のデータセットを読み取るのに800秒かかりました。
_seer9 <- read.fwf("~/data/rawdata.txt",
widths = cols,
header = FALSE,
buffersize = 250000,
colClasses = "character",
stringsAsFactors = FALSE))
_
Rの_data.table
_パッケージのfread
は、固定幅ファイルを解析しないことを除いて、ほとんどのデータ読み取り問題を解決するのに最適です。ただし、各行を単一の文字列(約500,000行、1列)として読み取ることができます。これには3〜5秒かかります。 (私はdata.tableが大好きです。)
_seer9 <- fread("~/data/rawdata.txt", colClasses = "character",
sep = "\n", header = FALSE, verbose = TRUE)
_
テキストファイルを解析する方法については、SOに関する良い投稿がいくつかあります。JHowardの提案 here を参照して、開始列と終了列のマトリックスを作成し、substr
を参照してください。データを解析します。strsplit
を使用するには、GSeeの提案 here を参照してください。このデータを使用してこれを機能させる方法がわかりませんでした(また、Michael Smithは、data.tableメーリングリストで私の能力を超えたsed
implement。 )これで、fread
とsubstr()
を使用して、約25〜30秒ですべてを実行できます。最後には時間がかかります(5秒?)。
_end_col <- cumsum(cols)
start_col <- end_col - cols + 1
start_end <- cbind(start_col, end_col) # matrix of start and end positions
text <- lapply(seer9, function(x) {
apply(start_end, 1, function(y) substr(x, y[1], y[2]))
})
dt <- data.table(text$V1)
setnames(dt, old = 1:ncol(dt), new = seervars)
_
私はこれがこれ以上改善できるかどうか疑問に思っていますか?固定幅のファイルを読み取らなければならないのは私だけではないので、これを高速化できれば、さらに大きなファイル(数百万行)の読み込みも許容できるようになります。 parallel
の代わりにmclapply
をlapply
および_data.table
_と一緒に使用しようとしましたが、何も変更されませんでした。 (おそらくRの経験が浅いためです。)これを非常に高速に行うためにRcpp関数を作成できると思いますが、それは私のスキルセットを超えています。また、lapplyを使用していない可能性があり、適切に適用します。
私のdata.table実装(magrittr
チェーンを使用)は同じ時間を要します。
_text <- seer9[ , apply(start_end, 1, function(y) substr(V1, y[1], y[2]))] %>%
data.table(.)
_
誰かがこれの速度を改善するための提案をすることができますか?それともこれはそれが得られるのと同じくらい良いですか?
以下は、実際のデータにリンクするのではなく、R内に同様のdata.tableを作成するコードです。 331文字、500,000行にする必要があります。データ内の欠落フィールドをシミュレートするスペースがありますが、これは[〜#〜]ではありません[〜#〜]スペース区切りデータです。 (誰かが興味がある場合、私は生のSEERデータを読み取っています。)他の人を助ける場合に備えて、列幅(cols)と変数名(seervars)も含めます。これらは、SEERデータの実際の列と変数の定義です。
_seer9 <-
data.table(rep((paste0(paste0(letters, 1000:1054, " ", collapse = ""), " ")),
500000))
cols = c(8,10,1,2,1,1,1,3,4,3,2,2,4,4,1,4,1,4,1,1,1,1,3,2,2,1,2,2,13,2,4,1,1,1,1,3,3,3,2,3,3,3,3,3,3,3,2,2,2,2,1,1,1,1,1,6,6,6,2,1,1,2,1,1,1,1,1,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,5,4,10,3,3,2,2,2,3,1,1,1,1,2,2,1,1,2,1,9,5,5,1,1,1,2,2,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,1,4,1,4,1,1,3,3,3,3,2,2,2,2)
seervars <- c("CASENUM", "REG", "MAR_STAT", "RACE", "Origin", "NHIA", "SEX", "AGE_DX", "YR_BRTH", "PLC_BRTH", "SEQ_NUM", "DATE_mo", "DATE_yr", "SITEO2V", "LATERAL", "HISTO2V", "BEHO2V", "HISTO3V", "BEHO3V", "GRADE", "DX_CONF", "REPT_SRC", "EOD10_SZ", "EOD10_EX", "EOD10_PE", "EOD10_ND", "EOD10_PN", "EOD10_NE", "EOD13", "EOD2", "EOD4", "EODCODE", "TUMOR_1V", "TUMOR_2V", "TUMOR_3V", "CS_SIZE", "CS_EXT", "CS_NODE", "CS_METS", "CS_SSF1", "CS_SSF2", "CS_SSF3", "CS_SSF4", "CS_SSF5", "CS_SSF6", "CS_SSF25", "D_AJCC_T", "D_AJCC_N", "D_AJCC_M", "D_AJCC_S", "D_SSG77", "D_SSG00", "D_AJCC_F", "D_SSG77F", "D_SSG00F", "CSV_ORG", "CSV_DER", "CSV_CUR", "SURGPRIM", "SCOPE", "SURGOTH", "SURGNODE", "RECONST", "NO_SURG", "RADIATN", "RAD_BRN", "RAD_SURG", "SS_SURG", "SRPRIM02", "SCOPE02", "SRGOTH02", "REC_NO", "O_SITAGE", "O_SEQCON", "O_SEQLAT", "O_SURCON", "O_SITTYP", "H_BENIGN", "O_RPTSRC", "O_DFSITE", "O_LEUKDX", "O_SITBEH", "O_EODDT", "O_SITEOD", "O_SITMOR", "TYPEFUP", "AGE_REC", "SITERWHO", "ICDOTO9V", "ICDOT10V", "ICCC3WHO", "ICCC3XWHO", "BEHANAL", "HISTREC", "BRAINREC", "CS0204SCHEMA", "RAC_RECA", "RAC_RECY", "NHIAREC", "HST_STGA", "AJCC_STG", "AJ_3SEER", "SSG77", "SSG2000", "NUMPRIMS", "FIRSTPRM", "STCOUNTY", "ICD_5Dig", "CODKM", "STAT_REC", "IHS", "HIST_SSG_2000", "AYA_RECODE", "LYMPHOMA_RECODE", "DTH_CLASS", "O_DTH_CLASS", "EXTEVAL", "NODEEVAL", "METSEVAL", "INTPRIM", "ERSTATUS", "PRSTATUS", "CSSCHEMA", "CS_SSF8", "CS_SSF10", "CS_SSF11", "CS_SSF13", "CS_SSF15", "CS_SSF16", "VASINV", "SRV_TIME_MON", "SRV_TIME_MON_FLAG", "SRV_TIME_MON_PA", "SRV_TIME_MON_FLAG_PA", "INSREC_PUB", "DAJCC7T", "DAJCC7N", "DAJCC7M", "DAJCC7STG", "ADJTM_6VALUE", "ADJNM_6VALUE", "ADJM_6VALUE", "ADJAJCCSTG")
_
UPDATE:LaFは、生の.txtファイルから7秒未満で読み取り全体を実行しました。たぶんもっと速い方法があるかもしれませんが、私は何かがかなり良くできるとは思えません。素晴らしいパッケージ。
2015年7月27日更新これに対する小さな更新を提供したかっただけです。私は新しいreadrパッケージを使用し、readr :: read_fwfを使用して5秒でファイル全体を読み取ることができました。
_seer9_readr <- read_fwf("path_to_data/COLRECT.TXT",
col_positions = fwf_widths(cols))
_
また、更新されたstringi :: stri_sub関数は、base :: substr()の少なくとも2倍の速度です。したがって、freadを使用してファイルを読み取る(約4秒)後の各コードの解析に適用される上記のコードでは、143:変数の抽出にstringi :: stri_subを使用した場合、base :: substrの19に対して約8秒かかりました。したがって、freadとstri_subを実行するには、まだ約12秒しかかかりません。悪くない。
_seer9 <- fread("path_to_data/COLRECT.TXT",
colClasses = "character",
sep = "\n",
header = FALSE)
text <- seer9[ , apply(start_end, 1, function(y) substr(V1, y[1], y[2]))] %>%
data.table(.)
_
素晴らしいベンチマークとiotoolsパッケージを追加した@ -MichaelChiricoによる 下の回答 も参照してください。
(これと その他の主要な質問 の間で固定幅ファイルの効果的な読み取りについて)そのようなファイルを読み取るためのオファーにはかなりの量のオプションがあるので、いくつかのベンチマークが適切だと思います。
比較のために、次の大容量(400 MB)ファイルを使用します。これは、ランダムに定義されたフィールドと幅を持つランダムな文字の集まりです。
set.seed(21394)
wwidth = 400L
rrows = 1000000
#creating the contents at random
contents =
write.table(replicate(rrows, paste0(sample(letters, wwidth, replace = TRUE),
collapse = "")), file="testfwf.txt",
quote = FALSE, row.names = FALSE, col.names = FALSE)
#defining the fields & writing a dictionary
n_fields = 40L
endpoints = unique(c(1L, sort(sample(wwidth, n_fields - 1L)), wwidth + 1L))
cols = ist(beg = endpoints[-(n_fields + 1L)],
end = endpoints[-1L] - 1L)
dict = data.frame(column = paste0("V", seq_len(length(endpoints)) - 1L)),
start = endpoints[-length(endpoints)] - 1,
length = diff(endpoints))
write.csv(dict, file = "testdic.csv", quote = FALSE, row.names = FALSE)
これら2つのスレッド間で言及されている5つのメソッドを比較します(作成者が希望する場合は他のメソッドを追加します)。基本バージョン(read.fwf
)、in2csv
の結果をfread
(@AnandaMahtoの提案)、Hadleyの新しいreadr
(read_fwf
)、LaF
/ffbase
(@jwijfflsの提案)を使用したもの、および改善された(合理化された)質問作成者(@MarkDanese)が提案したバージョンのfread
とstringi
のstri_sub
を組み合わせたもの。
ベンチマークコードは次のとおりです。
library(data.table)
library(stringi)
library(readr)
library(LaF); library(ffbase)
library(microbenchmark)
microbenchmark(times = 5L,
utils = read.fwf("testfwf.txt", diff(endpoints), header = FALSE),
in2csv =
fread(paste("in2csv -f fixed -s",
"~/Desktop/testdic.csv",
"~/Desktop/testfwf.txt")),
readr = read_fwf("testfwf.txt", fwf_widths(diff(endpoints))),
LaF = {
my.data.laf =
laf_open_fwf('testfwf.txt', column_widths=diff(endpoints),
column_types = rep("character",
length(endpoints) - 1L))
my.data = laf_to_ffdf(my.data.laf, nrows = rrows)
as.data.frame(my.data)},
fread = fread(
"testfwf.txt", header = FALSE, sep = "\n"
)[ , lapply(seq_len(length(cols$beg)),
function(ii)
stri_sub(V1, cols$beg[ii], cols$end[ii]))])
そして出力:
# Unit: seconds
# expr min lq mean median uq max neval cld
# utils 423.76786 465.39212 499.00109 501.87568 543.12382 560.84598 5 c
# in2csv 67.74065 68.56549 69.60069 70.11774 70.18746 71.39210 5 a
# readr 10.57945 11.32205 15.70224 14.89057 19.54617 22.17298 5 a
# LaF 207.56267 236.39389 239.45985 237.96155 238.28316 277.09798 5 b
# fread 14.42617 15.44693 26.09877 15.76016 20.45481 64.40581 5 a
したがって、readr
とfread
+ stri_sub
は、最速でかなり競争力があるようです。ビルトインread.fwf
は明らかに敗者です。
ここでのreadr
の本当の利点は、列タイプを事前に指定できることです。 fread
を使用すると、後でconvertと入力する必要があります。
@AnandaMahtoの提案では、新しい勝者のように見えるものを含め、いくつかのオプションを追加しています。時間を節約するために、新しい比較で上記の最も遅いオプションを除外しました。新しいコードは次のとおりです。
library(iotools)
microbenchmark(times = 5L,
readr = read_fwf("testfwf.txt", fwf_widths(diff(endpoints))),
fread = fread(
"testfwf.txt", header = FALSE, sep = "\n"
)[ , lapply(seq_len(length(cols$beg)),
function(ii)
stri_sub(V1, cols$beg[ii], cols$end[ii]))],
iotools = input.file("testfwf.txt", formatter = dstrfw,
col_types = rep("character",
length(endpoints) - 1L),
widths = diff(endpoints)),
awk = fread(paste(
"awk -v FIELDWIDTHS='",
paste(diff(endpoints), collapse = " "),
"' -v OFS=', ' '{$1=$1 \"\"; print}' < ~/Desktop/testfwf.txt",
collapse = " "), header = FALSE))
そして新しい出力:
# Unit: seconds
# expr min lq mean median uq max neval cld
# readr 7.892527 8.016857 10.293371 9.527409 9.807145 16.222916 5 a
# fread 9.652377 9.696135 9.796438 9.712686 9.807830 10.113160 5 a
# iotools 5.900362 7.591847 7.438049 7.799729 7.845727 8.052579 5 a
# awk 14.440489 14.457329 14.637879 14.472836 14.666587 15.152156 5 b
したがって、iotools
は非常に高速で非常に一貫しているようです。
LaF
パッケージを使用できます。これは、固定幅の大きなファイルを処理するために作成されています(これもメモリに収まりきらない)。これを使用するには、最初にlaf_open_fwf
を使用してファイルを開く必要があります。その後、必要なデータを読み取る通常のデータフレームと同じように、結果のオブジェクトにインデックスを付けることができます。以下の例では、ファイル全体を読み取りますが、特定の列や行を読み取ることもできます。
library(LaF)
laf <- laf_open_fwf("foo.dat", column_widths = cols,
column_types=rep("character", length(cols)),
column_names = seervars)
seer9 <- laf[,]
(500,000ではなく)5000行を使用した例では、read.fwf
を使用して28秒、LaF
を使用して1.6秒かかりました。
追加(500,000ではなく)50,000行を使用する例では、read.fwf
を使用すると258秒、LaF
を使用すると7秒かかりました。
あなたが使っているOSはわかりませんが、これはLinuxで私にはかなり簡単に機能しました:
ステップ1:awk
のコマンドを作成して、ファイルをcsvに変換します
他のソフトウェアでもデータを使用する場合は、実際のcsvファイルに保存することができます。
myCommand <- paste(
"awk -v FIELDWIDTHS='",
paste(cols, collapse = " "),
"' -v OFS=',' '{$1=$1 \"\"; print}' < ~/rawdata.txt",
collapse = " ")
ステップ2:作成したコマンドでfread
を直接使用します
seer9 <- fread(myCommand)
私は明らかにあなたとJanよりも遅いシステムを使用しているので、これを計時していません:-)
昨日、この種のパーサーを作成しましたが、これはヘッダーファイルへの非常に特定の種類の入力用だったので、列幅を使用できるようにフォーマットする方法を示します。
最初にダウンロード 問題のツール 。
OS X Mavericks(私がコンパイルした場所)を使用している場合は、bin
ディレクトリからバイナリをダウンロードするか、src
に移動してclang++ csv_iterator.cpp parse.cpp main.cpp -o flatfileparser
を使用してコンパイルできます。
フラットファイルパーサーには2つのファイルが必要です。CSVヘッダーファイルでは、5つおきの要素で変数の幅が指定されています(これも、非常に具体的なアプリケーションが原因です)。
cols = c(8,10,1,2,1,1,1,3,4,3,2,2,4,4,1,4,1,4,1,1,1,1,3,2,2,1,2,2,13,2,4,1,1,1,1,3,3,3,2,3,3,3,3,3,3,3,2,2,2,2,1,1,1,1,1,6,6,6,2,1,1,2,1,1,1,1,1,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,5,4,10,3,3,2,2,2,3,1,1,1,1,2,2,1,1,2,1,9,5,5,1,1,1,2,2,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,1,4,1,4,1,1,3,3,3,3,2,2,2,2)
writeLines(sapply(c(-1, cols), function(x) paste0(',,,,', x)), '~/tmp/header.csv')
結果の~/tmp/header.csv
をflatfileparser
と同じディレクトリにコピーします。フラットファイルも同じディレクトリに移動すると、フラットファイルで実行できます。
./flatfileparser header.csv yourflatfile
yourflatfile.csv
が生成されます。パイピングを使用して、上記のヘッダーを手動で追加します(Bashの>>
)。
Hadleyの実験的な fastreadパッケージ を使用して、ファイル名をfastread::read_csv
に渡すと、data.frame
が生成されます。私は彼がfwf
ファイルをサポートしているとはまだ信じていません。