DataTable& Aggregateでの集計

解決


犬好き  2013-01-11 12:37:39  No: 143426

自力での解決をと思っていましたが、身も心も消耗して
きましたのでヘルプを求めてやってきました。
(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 23:51:15  No: 143427

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

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

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

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


犬好き  2013-01-12 00:45:59  No: 143428

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

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


YuO  2013-01-13 12:07:50  No: 143429

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

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


犬好き  2013-01-13 18:39:29  No: 143430

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

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


魔界の仮面弁士  2013-01-17 04:54:44  No: 143431

> すべての値の平均を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 09:31:35  No: 143432

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

悪戦苦闘中の名残です。

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

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


魔界の仮面弁士  2013-01-18 21:33:21  No: 143433

> あるいは、事前に「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-20 02:12:39  No: 143434

ご親切にありがとうございます。
サンプル集、そこそこ目を通しました。
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


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加