読者です 読者をやめる 読者になる 読者になる

雑記: XLConnect の読み込みで数式が NA になる場合

XLConnect でエクセルから R にデータを読み込みたいのに意図通りにいかないときとかあると思います。
今回、読み込み対象のセルが数式になっていて、読み込み結果が目的の数値ではなく NA になってしまいました。
読み込み対象が数式セルであっても大抵は数値セルのように読み込めると思います。
今回はおそらく以下の要因で読み込めなくなったようです(バージョン依存かもしれません)。

  • 数式が条件付き SUMPRODUCT() のとき、R 側で数式の再計算ができない。
    • なんか以下のような感じのやつ。"文房具" の売上だけ合計したいとか、"ある店舗" の "文房具" の売上だけ合計したいとかそういうときにつかうはず。
      =SUMPRODUCT((B2:B15=$A$1)*D2:D15)
      =SUMPRODUCT((B2:B15=$A$1)*(C2:C15=$A$2), D2:D15)

結論からいうと、数式の計算結果がキャッシュされた状態で保存しておき(普通は大丈夫なはず)、
キャッシュを読み取ることを明示的に指定すれば意図通り R からの読み込みができます。

  • readWorksheet() に useCachedValues=TRUE を指定します。→ これで解決すればOK
  • それで 0 になってしまう場合は、キャッシュが保存されていません(今回はこちらでした)。
    • エクセルを手で編集していて、エクセル上の「計算方法」の設定を何もいじっていなければ、まず数式の計算結果はキャッシュされた状態で保存されていると思います。ただ、スクリプトで自動編集した場合とか、「計算方法」を「手動」に設定している場合は、計算結果のキャッシュがない状態で保存されている可能性があります。たぶん。
    • このようにキャッシュがない場合にキャッシュを付けるには、ファイルを開き、読み込みたい数値が表示されているのを目で確認します。もしこの時点で表示すらされていなかったら「再計算実行」をします。その後ファイルを閉じると、何も編集していないのに「変更を保存しますか」などと訊かれるので、保存を選択します(訊かれない場合、おそらく既にキャッシュ済です)。
      新たにキャッシュが付くと、ファイルサイズが増えます。ただ数式セルの量によるとは思います。

例えば、'hogehoge' シートの B2, C2, D2 を読み込む場合は以下のようにします。
数値セルであれば、(*)の行を書かなくても普通に読み込めます。

library(methods)
library(XLConnect)

workbook <- loadWorkbook("hoge.xlsx")

data <- readWorksheet(object=workbook, sheet="hogehoge",
                      startCol=3, endCol=5,
                      startRow=2, endRow=2,
                      useCachedValues=TRUE, # (*)数式を再計算せず、キャッシュを読む
                      header=FALSE)
print(data)
  • 計算結果キャッシュなし & キャッシュ読み出し未指定 → NA NA NA ("Incompatible type" と警告)
  • 計算結果キャッシュなし & キャッシュ読み出しを指定 → 0 0 0
  • 計算結果キャッシュあり & キャッシュ読み出し未指定 → NA NA NA ("Incompatible type" と警告)
  • 計算結果キャッシュあり & キャッシュ読み出しを指定 → 101 102 98 (意図通りの数値)

キャッシュが元々ない場合に、キャッシュを付ける作業をわざわざしたくないのは山々ですが、よくわかりません。



なお、XLConnect には対象のシートに再計算を走らせる関数もあります。

setForceFormulaRecalculation(object=workbook, sheet="hogehoge", TRUE)

loadWorkbook() の後にこれをコールするようです。
ただ今回は、そもそも R 側で数式の再計算ができないので、これを呼んでも意味がありません。
というか useCachedValues=TRUE を指定しない場合は読み込み時にデフォルトで再計算をするはずなので、setForceFormulaRecalculation() のつかいどころは正直よくわかりません。