[[20210904135537]] 『複数条件の集計まとめ』(勉強中) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『複数条件の集計まとめ』(勉強中)

初めまして。
下記について、ピボットではなく、関数 及び マクロ、それぞれ出来ないか考えていますがうまくいきません。ご教授頂けますと幸いです。

シートが3つあります。(比較1・比較2・集計)

比較1シートと比較2シートの構造は全く同じです。(例として比較1シートを載せます)

<比較1>

    A   B       C       D       E       F       G       H       I
 1 項目	チーム	No.	予備	8/1	8/2	8/3	8/4	8/5
 2  AA	 a	1		100		1,000		500
 3  AA	 a	2		500	100			
 4  AA	 d	3			100		3,000	
 5  BB	 c	2		3,000			200	
 6  BB	 c	1		100				
 7  BB	 a	3		200			500	
 8  CC	 c	1		200	200			
 9  CC	 a	3		100	100		500	1,000
 10 CC	 d	1						
 11 DD	 d	3			100		200	
 12 DD	 c	1		200				
 13 DD	 c	3	        500

A列の項目は集計には関係ありません。B列のチーム、C列のNo.に応じた足し算を集計シートに実施したいというのが最終的なやりたいことです。

集計シートは下記のように、A列〜D列にチーム、E列〜G列にNo. を入力して、その入力した様々なパターンで集計したいです。

集計シートのA〜D列には集計したいチームを記入(複数の場合はB, C, D列に記載)
集計シートのE〜G列には集計したいNo.を記入(複数の場合はF, G列に記載)
outputとしてはH列に8/1の比較1シートの値を見て、集計シートのA〜G列の条件に一致するものを足し算したい。
I列には、同様に比較2シートでも足し算してきたい。
J列には、比較1-比較2の差分として、=IFERROR(H3-I3,H3)という計算式を入れている。
書ききれませんでしたが、K列以降には同じように、8/2, 8/3…と計算したい。

<集計シートの例>

     A   B    C    D    E    F    G     H      I     J   
 1 チーム1 チーム2 チーム3 チーム4 No.1 No.2 No.3  8/1    8/1   8/1    
 2                                    比較1  比較2  差分    
 3 a                    1    2    3    900    200   700		
 4 a    b    c    d     2             3,500   2,000 1,500
 5 b    c               2    3        3,500         3,000 
 6 c                    1    3        1,000   1,000 -200

自分で考えた計算式は、

H3 =SUMPRODUCT(SUMIFS(比較1!$E:$E,比較1!$B:$B,集計!$A3:$D3,比較!$C:$C,集計!$E3:$G3))
としましたが、うまく結果が合いません。恐らく、チーム1-No.1, チーム2-No.2, チーム3-No.3 という風にデータの列がそろっている条件は拾ってきているようなのですが、片方が空欄だと計算されないのか?とか、
条件がそろっている場合でも、一度計算したものが下の方に再び登場するとその分は拾っていない?等、良く分かっていません。
何か良い案はありますでしょうか?

また、関数以外にも、最終的にはマクロでも出来たらやってみたいのですが、
このような複雑な条件ですと難しいでしょうか?

何卒、ご教授の程、宜しくお願いいたします。

< 使用 Excel:Excel2013、使用 OS:Windows7 >


 コメントがつかないようなので前座です。

 当初の式が機能しない理由は、比較対象値がふたつあり、
 SUMIFSは、それらの直積(カルテシアン積)に対応するものを返すわけではなく、
       ( チーム==チーム1 かつNo. == No.1 )
 または( チーム==チーム2 かつNo. == No.2 )
 または( チーム==チーム3 かつNo. == No.3 )
 にマッチした結果の合計しか返さないからでしょう。

 一向に垢抜けしない案ですが、こんな形にしてはどうですか。

 <<集計シートのレイアウト>>
 式が簡単なコピーで対応できるように、レイアウトを以下のように変更します。

     A   B    C    D    E    F    G     H      I     J   
 1 チーム1 チーム2 チーム3 チーム4 No.1 No.2 No.3  8/1    8/2   8/3    
 2                                        
 3 a                    1    2    3     900                
 4                                      200 
 5                                      700    (以下、左に同じ)
 6 a    b    c    d     2             3,500 
 7                                    2,000 
 8                                    1,500 

 H3 =SUMPRODUCT(SUMIFS(比較1!E:E,比較1!$B:$B,$A3:$D3,比較1!$C:$C,$E3))
    +SUMPRODUCT(SUMIFS(比較1!E:E,比較1!$B:$B,$A3:$D3,比較1!$C:$C,$F3))
    +SUMPRODUCT(SUMIFS(比較1!E:E,比較1!$B:$B,$A3:$D3,比較1!$C:$C,$G3))
 H4 =SUMPRODUCT(SUMIFS(比較2!E:E,比較2!$B:$B,$A3:$D3,比較2!$C:$C,$E3))
    +SUMPRODUCT(SUMIFS(比較2!E:E,比較2!$B:$B,$A3:$D3,比較2!$C:$C,$F3))
    +SUMPRODUCT(SUMIFS(比較2!E:E,比較2!$B:$B,$A3:$D3,比較2!$C:$C,$G3))
 H5 = H3 - H4

 ・H3:H5を右に日数分だけコピー
 ・H3:5行目最終列(3行)をまとめて、下に、ケース毎領域に一回だけコピー

 >また、関数以外にも、最終的にはマクロでも出来たらやってみたいのですが、
 >このような複雑な条件ですと難しいでしょうか?
 マクロのほうがより複雑なものにも対応できると思います。可能でしょう。
 まあ、そこまですることもないでしょうね。
(γ) 2021/09/05(日) 13:51

昨日質問させて頂きましたものです。
完全に深みにはまっておりまして、どなたかご存じの方いましたら、宜しくお願いいたします。

昨日私が考えてうまくいかなかった下記式ですが、
H3 =SUMPRODUCT(SUMIFS(比較1!$E:$E,比較1!$B:$B,集計!$A3:$D3,比較!$C:$C,集計!$E3:$G3))

やはり、複数条件に対してOR条件にするのは無理なのかな…と思いました。
下記のように、例えばチーム名は1つ、No.が3つ というケースですと、うまくいきます。
H3 =SUMPRODUCT(SUMIFS(比較1!$E:$E,比較1!$B:$B,集計!$A3,比較!$C:$C,集計!$E3:$G3))

また、SUMPRODUCTではなく、SUM(SUMIFS〜 にして、OR条件の所を{ }で指定しようとしてもダメでした。
{ }内はセル参照したいので、〜CHOOSE({1;2;3;4},$A3:$D3)〜にしてみたり、色々試してみましたが、
どれもことごとくダメで(笑)

SUMPRODUCTのANDは「*」、ORは「+」の考えで計算出来ないかとも考えましたが、そうすると、考えられるすべての組み合わせで「+」と「*」を設定しないといけないのか? 

等々、完全に深みにはまってしまいました。
関数ではやはり厳しいでしょうか。

また、マクロでもぜひやってみたいのですが、どなたか詳しい方がいらっしゃいましたら、宜しくお願いいたします。

(勉強中) 2021/09/05(日) 13:53


回答しましたよ。こういうケースでは見過ごされることがままあります。
(γ) 2021/09/05(日) 13:55

γ様

申し訳ありません。確認前に13:53の文を送信してしまいました。
ありがとうございます。
教えて頂いた内容を理解し、ご提示して頂いた内容で正しく出力できました。
(チーム名をまとめて指定する方法、No.をまとめて指定する方法、双方で試してみましたがどちらも値が一致しました。)
大変ありがとうございました。OR条件の理解が足りていませんでした。今後気を付けたいと思います。

尚、実際はNo.が3つではなく10個あったり、Outputもご提示して頂いたものでないと式のコピーが大変であるというのは重々承知なのですが、現状Outputフォームを変える事ができません。
となりますと、現実的なのはマクロを使用するべきでしょうか。

どなたかこのようなケースのマクロの組み方をご存じの方がいらっしゃいましたら、引き続きよろしくお願いいたします。
(勉強中) 2021/09/05(日) 14:18


>実際はNo.が3つではなく10個あったり、
>Outputもご提示して頂いたものでないと式のコピーが大変であるというのは重々承知なのですが、
>現状Outputフォームを変える事ができません。
あ、そうですか?
10個あってどこが困りますか?
ユーザー定義関数を使えば、シートに書く式は単純なものになりますよ。
また、配置は中間表をつくって、最終の表から、それを参照させれば済む話です。

マクロはどれくらい作成された経験がありますか?
単純な繰り返しですけどねえ。
(γ) 2021/09/05(日) 14:48


γ様
お忙しい中、色々貴重なアドバイスありがとうございます。
ユーザー定義関数は知りませんでした。勉強してみます。

マクロはここ1か月ほど本やネットで調べたりして、まだ全然理解出来ていないレベルです。Loopや IFが何とか、、配列等はネットで見てなるほど、このようにやるのか…とおぼろげな感じであります。。
(勉強中) 2021/09/05(日) 15:17


やはり、この事例をマクロでやるとなるとdictionary等にキーワードを格納して配列で求める形になるのでしょうか?
一つ一つIFで条件一致するものをループしていくという方法は現実的ではないですか?

あとは、初心者的な考えですと、集計シートのRange("H3:H8").Formula= "今回教えて頂いた関数"
にして、式の範囲部分は変数等使用して出来たりしないか…とか思ってしまいますが、
マクロでやるならそのような使い方は皆さんはしないのでしょうか?
(勉強中) 2021/09/05(日) 19:57


 こんばんは ^^
連想配列、便利ですね。(#^ ^#)v
差引等する時はよく
使いますです。r1c1とかで書けばバリ簡単ですよ^^;
x=8
.Cells(3, x).Resize(UBound(w2, 1), 1).FormulaR1C1 = "=rc[-2]-rc[-1]"
x = x + 1
な感じで。でわ。。。m(_ _)m
(隠居Z) 2021/09/05(日) 20:50

今回の場合、チームが4までで、Noが1〜3までだと
差分の初回xは10かも
失礼致しました。^^;
m(__)m
(隠居Z) 2021/09/05(日) 20:55

隠居Z様
ありがとうございます!
やはり、連想配列でやるのがいいのですね。
まだそこまで理解出来ていませんが、勉強していきたいと思います。
r1c1の使い方も今記載頂いたコードをネットで見ながら解読してみましたが、
offsetで範囲を移動以外にもこのような使い方があるのですね。勉強になります。
resizeとUBoundの部分がまだよく解読出来ませんでしたが、今後使えるようになれるよに、頑張ります。。
(勉強中) 2021/09/05(日) 21:08

 最初にユーザー定義関数を示しておきます。
 手作業であれば、これで十分なはずです。
 表を当初提案どおりにすれば、これで終わりです。
 簡単なことは簡単に済ませた方がよいと思います。

 使い方は、
 =SUMIFS2(比較1!E:E,比較1!$B:$B,$A3:$D3,比較1!$C:$C,$E3:$G3)
 といった書き方です。

 Function SUMIFS2(sumRange As Range, cond1R As Range, rng1 As Range, _
                  cond2R As Range, rng2 As Range) As Long
     Dim v As Long
     Dim r As Range
     For Each r In rng2
         v = v + Application.SumProduct(Application.SumIfs(sumRange, cond1R, rng1, cond2R, r))
     Next
     SUMIFS2 = v
 End Function

(γ) 2021/09/06(月) 07:55


 参考までに、マクロ案です。

 (1)標準モジュールにコピー
 (2)条件の数(下記のn1,n2)は手動でセットしてください。(手抜き)
 (3)日付や、比較1,比較2,差などの見出しはセットされているものとします。
 (4)testを実行すると、結果が"集計"シートに書き込まれる(はず)。

 Option Explicit

 Const n1 As Long = 4    ' 条件指定に用いる チームの数  ■要修正
 Const n2 As Long = 3    ' 条件指定に用いる No    の数   ■要修正

 Dim wsT As Worksheet    '集計シート
 Dim ws1 As Worksheet    'データシート
 Dim ws2 As Worksheet
 Dim mat()       As Long '結果配列
 Dim num_of_days As Long
 Dim dic1 As Object
 Dim dic2 As Object

 Sub test()
     Dim wslastRow As Long
     Dim ws1LastColumn As Long
     Dim wsTlastRow As Long
     Dim v1 As Variant
     Dim v2 As Variant
     Dim k As Long
     Dim j As Long

     Set wsT = Worksheets("集計")
     Set ws1 = Worksheets("比較1")
     Set ws2 = Worksheets("比較2")
     Set dic1 = CreateObject("Scripting.Dictionary")
     Set dic2 = CreateObject("Scripting.Dictionary")

     wsTlastRow = wsT.Cells(Rows.Count, "A").End(xlUp).Row

     ws1LastColumn = ws1.Cells(2, Columns.Count).End(xlToLeft).Column
     num_of_days = ws1LastColumn - 4

     ReDim mat(1 To wsTlastRow - 2, 1 To num_of_days * 3)

     ' 比較1,比較2の値を配列に持つ
     wslastRow = ws1.Cells(Rows.Count, "B").End(xlUp).Row
     v1 = ws1.Range(ws1.Cells(3, 1), ws1.Cells(wslastRow, 4 + num_of_days)).Value
     wslastRow = ws2.Cells(Rows.Count, "B").End(xlUp).Row
     v2 = ws2.Range(ws2.Cells(3, 1), ws2.Cells(wslastRow, 4 + num_of_days)).Value

     '集計の各パターンについて繰り返し
     For k = 3 To wsTlastRow
         dic1.RemoveAll
         dic2.RemoveAll
         '条件をもとにdicを作成
         For j = 1 To n1
             If wsT.Cells(k, j) <> "" Then dic1(CStr(wsT.Cells(k, j).Value)) = Empty
         Next
         For j = 1 To n2
             If wsT.Cells(k, n1 + j) <> "" Then dic2(CStr(wsT.Cells(k, n1 + j).Value)) = Empty
         Next
         Call myCount(k, v1, 1)  '条件にあった比較1データを抽出し、matに加算
         Call myCount(k, v2, 2)  '条件にあった比較2データを抽出し、matに加算
     Next
     Call 差額算出

     '結果書込
     wsT.Cells(3, n1 + n2 + 1).Resize(UBound(mat, 1), UBound(mat, 2)) = mat
 End Sub

 Function myCount(k As Long, v As Variant, pos As Long)
     'k行目にあるパターンについて、条件に合う数値をshの各データから検索し、
     '結果を、配列matのpos列から3列ごとに加算
     Dim s1 As String
     Dim s2 As String
     Dim j As Long, p As Long

     '条件にあったデータをmatに加算
     For j = LBound(v, 1) To UBound(v, 1)
         s1 = v(j, 2)
         s2 = v(j, 3)
         If dic1.Exists(s1) Then         ' チーム条件
             If dic2.Exists(s2) Then     ' No条件
                 For p = 1 To num_of_days
                     mat(k - 2, pos + 3 * (p - 1)) _
                     = mat(k - 2, pos + 3 * (p - 1)) + v(j, 4 + p)
                 Next
             End If
         End If
     Next
 End Function
 Function 差額算出()
     Dim j&, k&
     For j = 1 To UBound(mat, 1)
         For k = 1 To num_of_days
             mat(j, 3 + 3 * (k - 1)) = mat(j, 1 + 3 * (k - 1)) - mat(j, 2 + 3 * (k - 1))
         Next
     Next
 End Function

(γ) 2021/09/06(月) 07:56


γ様
お忙しい中、大変ありがとうございます。
ユーザー定義関数も昨日教えて頂いた後、使いこなすととても便利だなぁ…と思い、あれこれ試してみてましたが、
今回のケースだとどう設定すればいいのかがいまいち分からず昨日は時間切れでした。大変勉強になります。

今まだ出先で実際に試せていませんが、マクロのコードもこの後じっくり解読させて頂いて、今後連想配列はぜひ完全マスター出来るようにしていきたいなと思っております。
本当にありがとうございます。教えて頂いた考え方を今後使いこなせるように、頑張っていきたいと思います。
(勉強中) 2021/09/06(月) 19:31


 >また、配置は中間表をつくって、最終の表から、それを参照させれば済む話です。

 これは以下のようなことです。

 	A	B	C	D	E	F	G	H	I	J	K	L	M
 1													
 2	チーム1	チーム2	チーム3	チーム4	No.1	No.2	No.3	8/1日	8/2	8/3	8/4	8/5	
 3	a				1	2	3	900	200	1000	1000	1500	
 4	a	b	c	d	2			3500	100	0	200	0	
 5	b	c			2	3		3500	0	0	200	0	
 6	c				1	3		1000	200	0	0	0	
 7													
 8	a				1	2	3	90	20	100	100	150	
 9	a	b	c	d	2			350	10	0	20	0	
 10	b	c			2	3		350	0	0	20	0	
 11	c				1	3		100	20	0	0	0	
 12													
 13								8/1			8/2		
 14	a				1	2	3	900	90	810	200	20	180
 15	a	b	c	d	2			3500	350	3150	100	10	90
 16	b	c			2	3		3500	350	3150	0	0	0
 17	c				1	3		1000	100	900	200	20	180
                        (なお、比較2データは、比較1*0.1とした検証用のため仮数値)

 11行目までは、ユーザー定義関数SUMIFS2を使えば作成できますね。

 どうしても3列おきにしたいということなら、
 H14: =INDEX($H$3:$L$6,ROW($A1),1+INT((COLUMN()-8)/3))
 I14: =INDEX($H$8:$L$11,ROW($A1),1+INT((COLUMN()-9)/3))
 J14: =H14-I14
 などとしておいて
 H14:J14をH14:V17へコピーペイストするだけです。

 (1)ユーザー定義関数の使用
 (2)作業領域を躊躇わずに使用すること
 (3)それぞれは簡単な部品を組み合わせること
 こうした考え方をとれば、一足飛びに完成品にしようと悩むよりも簡単ですし、
 思考の節約になるはずです。

 なお、マクロでのdictionaryはこのケースで必須なわけではなく、
 ワークシート関数 COUNTIFを使って、>0 で判定させたり、
 Application.MATCH を使って、エラーになるかならないかで判定してもよいと思います。
 色々な方法があります。

 # 貼付処理まちがえていたので修正。(0:12)

(γ) 2021/09/06(月) 23:56


γ様
重ね重ね、ご丁寧な説明、大変ありがとうございます。
中間表の作成も関数のご提示も非常に勉強になりました。
三列ごとに…もこのように計算させれば可能な事、目から鱗でした。。

関数にしても、マクロにしてもγ様のような柔軟な発想が必須ですね。
それぞれの事例で何が適切な方法なのか、まだよく理解出来ていない初心者で駆け出しですが、
教えて頂いた事無駄にしないように、また引き出しを増やしていけるように、頑張っていきたいと思います。
大変、ありがとうございましたm(_ _)m!!
(勉強中) 2021/09/07(火) 04:46


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.