Bayes Server で missing valueに対応するには?


# missing value

欠損値を扱う場合、補完をする場合とそのままNAとして扱う場合があります。
bayesean networkの場合はそれ自身が補完に利用することもできるため、NAとしてそのまま渡したい場合があります。

Bayes Serverでは以下のように、欠損値をそのまま扱うことができます。
基本的にはEvidenceがすべてそろわなくても、学習、推論ができるという事なので、他のモデルよりも欠損値の扱いは、straightだと思います。

[Tutorial 7 - Missing data](https://www.bayesserver.com/docs/walkthroughs/walkthrough-7-missing-data#Data)

# GUI では

例えば、Tutorialにあるような、空白セルのあるExcelファイルを読み込んでも大丈夫です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/146939/62fbf208-4b52-8f6e-c878-c1291d81f6e1.png)

Batchクエリーなどの画面で見られますデータはこのように空白になったまま読み込まれています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/146939/e2078cd0-65f7-08fd-fd80-8b882370d9d9.png)


# プログラム(C#)では

ネイティブの[C#のコード](https://www.bayesserver.com/code/csharp/structural-learning-cs)を見ると、以下のように まずdataReaderCommandというものでevidenceReaderCommandに渡すDataTableを作成しています。

``` C#
            var dataReaderCommand = CreateDataReaderCommand();
            var variableReferences = network.Variables.Select(v => new VariableReference(v, ColumnValueType.Name, v.Name)).ToArray();
            var evidenceReaderCommand = new EvidenceReaderCommand(
                dataReaderCommand,
                variableReferences,
                new ReaderOptions());
```

CreateDataReaderCommandという関数は以下のように[DataTable](https://docs.microsoft.com/en-us/dotnet/api/system.data.datatable?redirectedfrom=MSDN&view=netframework-4.7.2)を単純に作成しているだけのように見えます。

``` C#
            var data = new DataTable();
            data.Columns.Add("A", typeof(string));
            data.Columns.Add("B", typeof(string));
            data.Columns.Add("C", typeof(string));
            data.Rows.Add("True", "False", "True");
            data.Rows.Add("True", "True", "True");
```

[DataTable](https://docs.microsoft.com/en-us/dotnet/api/system.data.datatable?redirectedfrom=MSDN&view=netframework-4.7.2)は、[DataRow](https://docs.microsoft.com/en-us/dotnet/api/system.data.datarow?view=netframework-4.7.2)を[コレクション](https://docs.microsoft.com/en-us/dotnet/api/system.data.datarowcollection?view=netframework-4.7.2)の要素として持っていますが、DataRow自体はコレクションではなくて[IItemArray](https://docs.microsoft.com/en-us/dotnet/api/system.data.datarow.itemarray?view=netframework-4.7.2)というインターフェイスを持っているだけのようです。
Columnが先に指定されているわけですから、列方向には、NULLを要素に入れてもいけそうな雰囲気があります。

単純なList<>などのコレクションだと普通はNULLは入らないのですが、どうやって実装するべきかは良くわかりません。（C#のdoubleにはNaNがあるんですが、これを使うとはちょっと思えない）

実はC#は使っていないので、ここは宿題にします。たぶんNULLいけそうな雰囲気ってことだけ。

# プログラム（R)では

Rから呼び出しているのですが、まずそのラップ先であるJavaはどうか？
[同じコードのJava版](https://www.bayesserver.com/code/java/structural-learning-java)を見ますと、以下のように、同じようなインターフェイスを持っているようです。

``` Java
        DataTable data = new DataTable();
        DataColumnCollection columns = data.getColumns();
        columns.add("A", String.class);
        columns.add("B", String.class);
        columns.add("C", String.class);
        DataRowCollection rows = data.getRows();
        rows.add("True", "False", "True");
        rows.add("True", "True", "True");
```

Rから同じことをするには、[ここ](https://www.bayesserver.com/code/r/structural-learning-r)にあるように、まず同じdata.frameを作ります。
注意点はRのdata.frameにはNULLは入れられず、NAが欠損値として扱われることです。

RからJavaラッパーを呼ぶ[ヘルパーコード](https://www.bayesserver.com/code/r/setup-r)が公開されていますが、以下のRのdata.frameからJavaラッパー経由でdataTableを作る関数が、ややおおざっぱすぎます。

``` R
toDataTable <- function(df) {
  
  dt <- new(DataTable)
  
  dfTypes <- sapply(df, typeof)
  dfClasses <- sapply(df, class)
  dfNames <- names(df)
  
  columnCount <- length(dfNames)
  
  for (i in 1:columnCount)
  {
    dt$getColumns()$add(dfNames[i], toJavaClass(dfTypes[i], dfClasses[i]))
  }
  
  for (r in 1:nrow(df))
  {
    values <- lapply(df[r,], function(x) {
      return(toJavaObject(x))
    })
    
    values <- .jarray(values, contents.class = "java.lang.Object")
    dt$getRows()$add(values)
  }
  
  return(dt)
  
}

```

このままNA入りのdata.frameを渡すと、なぜか”NaN入りのDataTableは受け付けないぞ！"というエラーで怒られます。


これだと、toJavaObject()内では、NAを無理やりJavaのNAに変換しようとするようなので、うまくNULLとして伝わっていないようです。
差し当たり、以下のように欠損値ぽいものが来たら、全部NULLに変換してしまうことにしたら、うまくいきました。

```R
        values <- lapply(df[r,], function(x) {
            if (!is.null(x) & !is.nan(x) & !is.na(x)) {
                return(toJavaObject(x))
            } else {
                return(NULL)
            }
        })
```

本当にNULLでいいのかどうか、C#で叩いてみて検証も後日行いたいと思います。