DataTable& Aggregateでの集計

解決


犬好き  2013-01-11 03:37:39  No: 143426  IP: [192.*.*.*]

自力での解決をと思っていましたが、身も心も消耗して
きましたのでヘルプを求めてやってきました。
(VisualBasic2010 Express)

DataTableの2列目のすべての値の平均をAggregate句を
使って求めたいのですが、どなたかご教授願えれば幸いです。
Private Sub hoge()   
        Dim TB As New DataTable
        TB.Columns.Add("名前", GetType(String))
        TB.Columns.Add("体重", GetType(Double))
        TB.Rows.Add("鈴木", 65.2)
        TB.Rows.Add("佐藤", 75.5)
        TB.Rows.Add("小林", 49.5)
        TB.Rows.Add("高橋", 63.2)

        Dim Sum_Wt as Double

        Sum_Wt= Aggregate wt As XXXX In YYYY Into Average()
        '↑のXXXX  と YYYY  のところの記述がわからないです。
End sub

編集 削除
YuO  2013-01-11 14:51:15  No: 143427  IP: [192.*.*.*]

まずは,MSDNの記述から。
http://msdn.microsoft.com/ja-jp/library/vstudio/bb531251.aspx

XXXXは列挙される型になります。
今回の場合は,DataRowになります。

次に,YYYYの部分は元になるコレクション,つまりはTB.Rowsになります。

最後に,Averageの中に,どの項目を使うのかを示す式を入れる必要があります。
wt("体重")をDoubleにキャストすることになるかと思います。

編集 削除
犬好き  2013-01-11 15:45:59  No: 143428  IP: [192.*.*.*]

>最後に,Averageの中に,どの項目を使うのかを示す式を入れる必要があります。
>wt("体重")をDoubleにキャストすることになるかと思います。

お手上げです。こんなのしか思いつきません。↓
Average("SELECT 体重 FROM Ctype(TB.Rows,Double)")

編集 削除
YuO  2013-01-13 03:07:50  No: 143429  IP: [192.*.*.*]

その「SELECT云々」はどこから出てきたのですか。

LINQはコンパイル時に全てを式にします。
なので,文字列で式を指定するようなことはありません。

編集 削除
犬好き  2013-01-13 09:39:29  No: 143430  IP: [192.*.*.*]

今はAggregateではなく、とりあえずTB.Compute("sum(体重)","")で済ませ
ました。For〜Eachでもいいんですが・・。
恥ずかしながら「LINQ」という言葉を最近知りました。
時間を見つけてLINQについて勉強するつもりです。

ご教授ありがとうございました。

編集 削除
魔界の仮面弁士  2013-01-16 19:54:44  No: 143431  IP: [192.*.*.*]

> すべての値の平均をAggregate句を使って求めたいのですが
> Dim Sum_Wt as Double
平均なのに、変数名が Sum なのは何故でしょうか?


> Sum_Wt= Aggregate wt As XXXX In YYYY Into Average()
> '↑のXXXX  と YYYY  のところの記述がわからないです。

Into 句を見直した方が良いでしょう。

提示された構文だと、XXXX は「Double」で済みますが、
YYYY の部分が、「TB.AsEnumerable().Select(Function(r) CDbl(r!体重))」
などのような複雑な構文になってしまいます。


Into 句を見直した書き方としては、こんな感じ。

Dim Ave1 As Double = Aggregate row In TB Into Average(row.Field(Of Double)("体重"))


あるいは、こんな書き方もあります。

Dim Ave2 As Double = (From row In TB Select row.Field(Of Double)("体重")).Average()


Linq を使わず、DataTable の Compute メソッドを使う手もあります。
この方法だと、より古いバージョンの VB.NET でも算出できます。

Dim Ave3 As Double = CDbl(TB.Compute("Avg(体重)", ""))


いずれの方法を使うにしろ、件数が 0 だった場合の扱いに注意してください。

たとえば TB.Compute を使った方法では、0件の場合の戻り値が
DBNull になるため、その結果を CDbl すると変換エラーとなります。
また、上記 Ave1, Ave2 の構文も、0 件の時は要素なしエラーとなります。


データ件数が 0 の場合も考慮するためには、このような書き方ができます。

Dim Ave0 As Double = (From row In TB Select row.Field(Of Double)("体重")).DefaultIfEmpty().Average()

あるいは、事前に「If TB.Count > 0 Then」などの判定を入れるのも手です。

編集 削除
犬好き  2013-01-17 00:31:35  No: 143432  IP: [192.*.*.*]

>>Dim Sum_Wt as Double
>平均なのに、変数名が Sum なのは何故でしょうか?

悪戦苦闘中の名残です。

Aggregateの件ですが、具体例を示していただきありがとうございました。

昔VB6でSQL文でExcelの巨大ファイルを処理した
ことがあるのでLinqはSQL文っぽいなぁという感じがしています。
その後も暇を見つけては調べていましたが自力での解決には
至っておらず、半ば諦めかけておりました。
MSDNで調べても「わけわかめ」でついついググって
「そのものズバリ!」のサンプルのコピペに頼ってしまいます。
みなさんとグーグルが私の先生です。
私はネット環境無しでは何も出来ないと思っています。
上級者とそうでない人とで、「見えているもの」がこれほど
違う世界はないのではないかと思い始めています。

編集 削除
魔界の仮面弁士  2013-01-18 12:33:21  No: 143433  IP: [192.*.*.*]

> あるいは、事前に「If TB.Count > 0 Then」などの判定を入れるのも手です。
訂正:

型付 DataSet/DataTable なら、If TB.Count > 0 Then でも良いですが、
素の DataTable の場合は、If TB.Rows.Count > 0 Then もしくは
If TB.AsEnumerable().Any() Then などを使ってみてください。


> 「そのものズバリ!」のサンプルのコピペに頼ってしまいます。
VB.NET 向け LINQ サンプル集
http://msdn.microsoft.com/en-us/vstudio/bb688088.aspx

編集 削除
犬好き  2013-01-19 17:12:39  No: 143434  IP: [192.*.*.*]

ご親切にありがとうございます。
サンプル集、そこそこ目を通しました。
Linqの素晴らしさを実感しました。
こんなこともできるんですね。驚きです。↓
Public Sub Linq78()
    Dim numbers() As Integer = {5, 4, 1, 3, 9, 8, 6, 7, 2, 0}

    Dim numSum = numbers.Sum()

    Console.WriteLine("The sum of the numbers is " & numSum)
End Sub

編集 削除