web-dev-qa-db-ja.com

パッケージreadxlを使用してxlsxデータをRにインポートする際の列タイプの指定

_R 3.2.1patched_の下にあるパッケージ_readxl 0.1.0_を使用して、xlsx 2007テーブルを_Windows 7 64_にインポートしています。テーブルのサイズは、25,000行x 200列程度です。

関数read_Excel()はごちそうを扱います。私の唯一の問題は、列クラス(データ型)をまばらに配置された列に割り当てることです。たとえば、特定の列が20,000行のNAである場合、行20,001で文字値を取得します。列の最初のn行をスキャンしてNAsのみを検索すると、read_Excel()はデフォルトで列タイプ数値に表示されます。問題の原因となるデータは、数値が割り当てられた列の文字です。エラー制限に達すると、実行が停止します。実際には疎な列のデータが必要なので、エラー制限を高く設定しても解決策にはなりません。

スローされた警告を確認することで、問題のある列を特定できます。また、read_Excel()には、パッケージドキュメントに従って引数_col_types_を設定することにより、列のデータ型をアサートするオプションがあります。

スプレッドシートから推測するNULLまたはblanknumericdateまたはtextを含む文字ベクトル。

しかし、これは、問題のある列に対応する少数の位置にblanktextが含まれるほぼすべての位置に長さ200のベクターを構築する必要があることを意味しますか?

おそらく数行のRコードでこれを行う方法があるでしょう。必要な長さのベクトルを作成し、blanksで埋めます。たぶんtextに強制される列の数を含む別のベクトルかもしれません...それからread_Excel()を呼び出すことが可能です望んだ通りに。

私はどんな提案にも感謝します。

前もって感謝します。

17
jackw19

readxlバージョン1.x以降の新しいソリューション:

現在推奨されている回答の解決策 は、使用されているパッケージ内部関数readxl:::xlsx_col_typesが存在しないため、readxlの0.1.0より新しいバージョンでは動作しなくなりました。

新しいソリューションは、新しく導入されたパラメーターguess_maxを使用して、列の適切なデータ型を「推測」するために使用される行の数を増やすことです。

read_Excel("My_Excel_file.xlsx", sheet = 1, guess_max = 1048576)

値1,048,576は、現在Excelでサポートされている最大行数です。Excelの仕様を参照してください。 https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043 -467e-8e27-269d656771c

PS:データタイプを推測するためにすべての行を使用してパフォーマンスを気にする場合:read_Excelはファイルを1回だけ読み取るようで、推測はメモリ内で行われるため、保存された作業と比較してパフォーマンスのペナルティは非常に小さくなります。

7
R Yoda

同様の問題が発生しました。

私の場合、空の行と列がセパレータとして使用されました。そして、シートには多くの表(異なる形式)が含まれていました。そう、 {openxlsx}および{readxl}パッケージはこの状況に適さないため、openxlsxは空の列を削除します(この動作を変更するパラメーターはありません)。 Readxlパッケージは説明どおりに機能し、一部のデータが失われる可能性があります。

結果として、大量のExcelデータを自動的に処理する場合の最善の解決策は、「テキスト」形式を変更せずにシートを読み取り、ルールに従ってdata.framesを処理することです。

この関数は、変更なしでシートを読み取ることができます(@ jack-waseyに感謝)。

loadExcelSheet<-function(Excel.file, sheet)
{
  require("readxl")
  sheets <- readxl::Excel_sheets(Excel.file)
  sheet.num <- match(sheet, sheets) - 1
  num.columns <- length(readxl:::xlsx_col_types(Excel.file, sheet =   sheet.num,
                                              nskip = 0, n = 1))

  return.sheet <- readxl::read_Excel(excel.file, sheet = sheet,
                                col_types = rep("text", num.columns),
                                col_names = F)
  return.sheet 
}
6
Stanislav

それは、データが異なる列の異なる場所でスパースであるかどうか、およびデータがどの程度スパースであるかによって異なります。行を増やしても解析が改善されないことがわかりました。大部分は空白のままで、後で日付などになっても、テキストとして解釈されます。

回避策の1つは、Excelテーブルの最初のデータ行を生成してすべての列の代表的なデータを含め、それを使用して列タイプを推測することです。元のデータをそのまま残したいので、これは好きではありません。

スプレッドシートのどこかに完全な行がある場合の別の回避策は、nskipの代わりにnを使用することです。これは、列の推測の開始点を示します。たとえば、データ行117にデータの完全なセットがあるとします。

readxl:::xlsx_col_types(path = "a.xlsx", nskip = 116, n = 1)

名前空間で関数を編集しなくても、関数を直接呼び出すことができることに注意してください。

次に、スプ​​レッドシートタイプのベクトルを使用して、read_Excelを呼び出すことができます。

col_types <- readxl:::xlsx_col_types(path = "a.xlsx", nskip = 116, n = 1)
dat <- readxl::read_Excel(path = "a.xlsx", col_types = col_types)

その後、まだ間違っている列を手動で更新できます。

4
Jack Wasey

source を確認すると、推測された列の型を返すRcpp呼び出しがあることがわかります。

xlsx_col_types <- function(path, sheet = 0L, na = "", nskip = 0L, n = 100L) {
    .Call('readxl_xlsx_col_types', PACKAGE = 'readxl', path, sheet, na, nskip, n)
}

デフォルトでは、nskip = 0L, n = 100Lは最初の100行をチェックして列タイプを推測していることがわかります。 nskipを変更してヘッダーテキストを無視し、nを増やすことができます(実行時間が大幅に遅くなります)。

col_types <-  .Call( 'readxl_xlsx_col_types', PACKAGE = 'readxl', 
                     path = file_loc, sheet = 0L, na = "", 
                     nskip = 1L, n = 10000L )

# if a column type is "blank", no values yet encountered -- increase n or just guess "text"
col_types[col_types=="blank"] <- "text"

raw <- read_Excel(path = file_loc, col_types = col_types)

.Rcppを確認しないと、nskip = 0Lがヘッダー行(C++カウントの0番目の行)をスキップするか、行をスキップしないかがすぐにわかりません。データセットの行をスキップしても列タイプ全体の推測には影響しないため、nskip = 1Lを使用することで曖昧さを回避しました。

2
C8H10N4O2

ソースを読むと、Rcppに実装されている関数xls_col_typesまたはxlsx_col_typesによって列タイプが推測されているように見えますが、デフォルトは次のとおりです。

xls_col_types <- function(path, na, sheet = 0L, nskip = 0L, n = 100L, has_col_names = FALSE) {
    .Call('readxl_xls_col_types', PACKAGE = 'readxl', path, na, sheet, nskip, n, has_col_names)
}

xlsx_col_types <- function(path, sheet = 0L, na = "", nskip = 0L, n = 100L) {
    .Call('readxl_xlsx_col_types', PACKAGE = 'readxl', path, sheet, na, nskip, n)
}

私のC++は非常に錆びていますが、n=100Lは読み取る行数を指示するコマンドのようです。

これらはエクスポートされていない関数なので、次の場所に貼り付けます。

fixInNamespace("xls_col_types", "readxl")
fixInNamespace("xlsx_col_types", "readxl")

そして、ポップアップでn = 100Lをより大きな数値に変更します。次に、ファイルのインポートを再実行します。

2
jeremycg

列タイプを推測するための内部機能は、スキャンする行の数に設定できます。しかし、read_Excel()はそれを実装していません(まだ?)。

以下の解決策は、デフォルトですべての行に設定される引数_n_max_を使用して、元の関数read_Excel()を書き換えたものです。想像力が不足しているため、この拡張関数は_read_Excel2_と呼ばれます。

_read_Excel_を_read_Excel2_に置き換えるだけで、すべての行で列タイプを評価できます。

_# Inspiration: https://github.com/hadley/readxl/blob/master/R/read_Excel.R 
# Rewrote read_Excel() to read_Excel2() with additional argument 'n_max' for number
# of rows to evaluate in function readxl:::xls_col_types and
# readxl:::xlsx_col_types()
# This is probably an unstable solution, since it calls internal functions from readxl.
# May or may not survive next update of readxl. Seems to work in version 0.1.0
library(readxl)

read_Excel2 <- function(path, sheet = 1, col_names = TRUE, col_types = NULL,
                       na = "", skip = 0, n_max = 1050000L) {

  path <- readxl:::check_file(path)
  ext <- tolower(tools::file_ext(path))

  switch(readxl:::Excel_format(path),
         xls =  read_xls2(path, sheet, col_names, col_types, na, skip, n_max),
         xlsx = read_xlsx2(path, sheet, col_names, col_types, na, skip, n_max)
  )
}
read_xls2 <- function(path, sheet = 1, col_names = TRUE, col_types = NULL,
                     na = "", skip = 0, n_max = n_max) {

  sheet <- readxl:::standardise_sheet(sheet, readxl:::xls_sheets(path))

  has_col_names <- isTRUE(col_names)
  if (has_col_names) {
    col_names <- readxl:::xls_col_names(path, sheet, nskip = skip)
  } else if (readxl:::isFALSE(col_names)) {
    col_names <- paste0("X", seq_along(readxl:::xls_col_names(path, sheet)))
  }

  if (is.null(col_types)) {
    col_types <- readxl:::xls_col_types(
      path, sheet, na = na, nskip = skip, has_col_names = has_col_names, n = n_max
    )
  }

  readxl:::xls_cols(path, sheet, col_names = col_names, col_types = col_types, 
                    na = na, nskip = skip + has_col_names)
}

read_xlsx2 <- function(path, sheet = 1L, col_names = TRUE, col_types = NULL,
                       na = "", skip = 0, n_max = n_max) {
  path <- readxl:::check_file(path)
  sheet <-
    readxl:::standardise_sheet(sheet, readxl:::xlsx_sheets(path))

  if (is.null(col_types)) {
    col_types <-
      readxl:::xlsx_col_types(
        path = path, sheet = sheet, na = na, nskip = skip + isTRUE(col_names), n = n_max
      )
  }

  readxl:::read_xlsx_(path, sheet, col_names = col_names, col_types = col_types, na = na,
             nskip = skip)
}
_

この拡張された推測のために、パフォーマンスに悪影響を及ぼす可能性があります。本当に大きなデータセットはまだ試していませんが、機能を確認するのに十分なだけ小さいデータを試しました。

1
Stefan Jansson