[[20200516175633]] 『配列を用いた参照と代入;その2』(アイス) ページの最後に飛ぶ

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

 

『配列を用いた参照と代入;その2』(アイス)

昨日は、[[20200515213649]]のご回答をくださりありがとうございました。
追加でもうひとつ質問させてください。
昨日と同じような以下のような反復測定データです。
測定回数順に"time"が1,2,3…と並びますが、
被験者毎に測定した回数はばらばら(id1は4回測定、id2は3回測定、id3は5回測定)です。
id3の次はid10,id11と続きます。
全被験者にどこかのタイミングで必ずC列"event"が1回のみありますが、このタイミングは被験者毎にばらばらです。
ある被験者は2回目かもしれないし、また別の被験者は初回かもしれません。
また、D列で各被験者の初回の測定を"baseline"として示しています。
E列"x"は評価したいパラメータで、
F列"delta_x_from_event"はある測定時点での"event"時からの変化量です。
例えば、F2=2-3、F3=3-3、F4=5-3、F5=9-3、F6=1-2、F7=2-2、…と計算します。
G列"delta_x_from_baseline"はある測定時点での"baseline"時からの変化量です。
例えば、G2=2-2、G3=3-2、G4=5-2、G5=9-2、G6=1-1、G7=2-1、…と計算します。

 ____A___B________C________D____________E________________F_______________________G______________
  1| id	 time	eventid	baselineid	x	delta_x_from_event	delta_x_from_baseline
  2|  1  1		baseline1	2		-1			 0
  3|  1	 2	event1			3		 0			 1
  4|  1	 3				5		 2			 3
  5|  1	 4				9		 6			 7
  6|  2	 1		baseline2	1		-1			 0
  7|  2	 2	event2			2		 0			 1
  8|  2	 3				4		 2			 3
  9|  3	 1		baseline3	6		-1			 0
 10|  3	 2				6		-1			 0
 11|  3	 3	event3			7		 0			 1
 12|  3	 4				8		 1			 2
 13|  3	 5				8		 1			 2
 14| 10	 1		baseline10	7		-2			 0
 15| 10	 2	event10			9		 0			 2
 16| 10	 3				4		-5			-3
 17| 11	 1		baseline11	7		-2			 0
 18| 11	 2				6		-3			-1
 19| 11	 3	event11			9		 0			 2
 20| 11	 4				5		-4			-2

最初は空白になっているこのF2:G20に変化量を計算して埋めることが目的です。
下記のように19行1列サイズの静的二次元配列を2つ作りました。

Sub test3()
Dim i As Long, j As Long 'i,jは二次元配列のインデックス番号、iは行、jは列と定義
Dim Rng_event As Range, Rng_baseline As Range
Dim delta_x_from_event As Variant, delta_x_from_baseline As Variant
ReDim delta_x_from_event(18, 0), delta_x_from_baseline(18, 0) '二次元配列と定義 配列サイズは19行1列
Dim eventid As String, baselineid As String

    For i = 0 To 18 '行要素数は19、0からスタート
        eventid = "event" & Cells(i + 2, 1).Value
        baselineid = "baseline" & Cells(i + 2, 1).Value
        Set Rng_event = Range(Cells(2, 3), Cells(20, 3)).Find(eventid)
        Set Rng_baseline = Range(Cells(2, 4), Cells(20, 4)).Find(baselineid)
            delta_x_from_event(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_event.Row, 5).Value
            delta_x_from_baseline(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_baseline.Row, 5).Value
    Next i
    Range(Cells(2, 6), Cells(20, 6)) = delta_x_from_event  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
    Range(Cells(2, 7), Cells(20, 7)) = delta_x_from_baseline  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
End Sub

上記コードをトライすると、

 ____A___B________C________D____________E________________F_______________________G______________
  1| id	 time	eventid	baselineid	x	delta_x_from_event	delta_x_from_baseline
  2|  1  1		baseline1	2		-1			-5
  3|  1	 2	event1			3		 0			-4
  4|  1	 3				5		 2			-2
  5|  1	 4				9		 6			 2
  6|  2	 1		baseline2	1		-1			 0
  7|  2	 2	event2			2		 0			 1
  8|  2	 3				4		 2			 3
  9|  3	 1		baseline3	6		-1			 0
 10|  3	 2				6		-1			 0
 11|  3	 3	event3			7		 0			 1
 12|  3	 4				8		 1			 2
 13|  3	 5				8		 1			 2
 14| 10	 1		baseline10	7		-2			 0
 15| 10	 2	event10			9		 0			 2
 16| 10	 3				4		-5			-3
 17| 11	 1		baseline11	7		-2			 0
 18| 11	 2				6		-3			-1
 19| 11	 3	event11			9		 0			 2
 20| 11	 4				5		-4			-2

上記のように、
(G2,G3,G4,G5)=(-5,-4,-2,2)となります。
本来は、
(G2,G3,G4,G5)=(0,1,3,7)となってほしいのですが、この4つのセルのみ間違った結果を出力します。
delta_x_from_baselineはid1だけ、誤ってid10のbaseline値(すなわちE14セルの値7)を参照しているようです・・・(*)

試しに、元のデータをE14=100と書き換えて再度Sub test3()のコードをトライすると、
(G2,G3,G4,G5)=(-98,-97,-95,-91)と返されるので、
(*)は正しいと思います。

Sub test3()のコードのどこかで間違っているのでしょうか?

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


失礼致しました。
コードに一部の誤りがありました。
Dim i As Long, j As Long 'i,jは二次元配列のインデックス番号、iは行、jは列と定義

Dim i As Long 'iは二次元配列のインデックス番号
です。

Sub test3()
Dim i As Long 'iは二次元配列のインデックス番号
Dim Rng_event As Range, Rng_baseline As Range
Dim delta_x_from_event As Variant, delta_x_from_baseline As Variant
ReDim delta_x_from_event(18, 0), delta_x_from_baseline(18, 0) '二次元配列と定義 配列サイズは19行1列
Dim eventid As String, baselineid As String

    For i = 0 To 18 '行要素数は19、0からスタート
        eventid = "event" & Cells(i + 2, 1).Value
        baselineid = "baseline" & Cells(i + 2, 1).Value
        Set Rng_event = Range(Cells(2, 3), Cells(20, 3)).Find(eventid)
        Set Rng_baseline = Range(Cells(2, 4), Cells(20, 4)).Find(baselineid)
            delta_x_from_event(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_event.Row, 5).Value
            delta_x_from_baseline(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_baseline.Row, 5).Value
    Next i
    Range(Cells(2, 6), Cells(20, 6)) = delta_x_from_event  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
    Range(Cells(2, 7), Cells(20, 7)) = delta_x_from_baseline  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
End Sub

上記コードですが、結局本コード内にカウンタ変数jは現れませんので、結果は同じです。

(アイス) 2020/05/16(土) 18:15


> Set Rng_event = Range(Cells(2, 3), Cells(20, 3)).Find(eventid)

まったく、理解できていませんが
このFindは、何をしているのでしょうか。

(マナ) 2020/05/16(土) 18:26


間違えました。

>Set Rng_baseline = Range(Cells(2, 4), Cells(20, 4)).Find(baselineid)

こっちのFindでは、何をしていますか。

(マナ) 2020/05/16(土) 18:29


↑もう結構です。
コードを読んで理解しました。関係なかったです。

(マナ) 2020/05/16(土) 18:40


ステップイン(F8)で実行して確認できないものでしょうか

(マナ) 2020/05/16(土) 18:45


わかりました。やっぱり原因はFindですね。
完全一致で検索してください。

(マナ) 2020/05/16(土) 18:49


マナ様

ありがとうございます。
分かりづらく申し訳ございませんでした。
回答を書いておりましたが、私の返答が遅い間にマナ様にご解決いただきました。

> eventid = "event" & Cells(i + 2, 1).Value
> Set Rng_event = Range(Cells(2, 3), Cells(20, 3)).Find(eventid)
> delta_x_from_event(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_event.Row, 5).Value
の趣旨は、
各被験者において反復測定データ数(行数)はばらばら、eventが生じたタイミングもばらばらですので、各被験者ごとにeventが生じた行はどこか検索して、その行番号を取得します。
変化量の計算の際に、"各被験者ごとにeventが生じた行番号"のx値を利用しています。

ステップイン(F8)確認ですが、
代入する際に配列で一気に代入するのでワークシート上では、
なぜE14セルを参照してしまうのかわかりませんでした。

しかし、マナ様にご教示いただいたFindの完全一致で、

Sub test3()
Dim i As Long 'iは二次元配列のインデックス番号
Dim Rng_event As Range, Rng_baseline As Range
Dim delta_x_from_event As Variant, delta_x_from_baseline As Variant
ReDim delta_x_from_event(18, 0) '二次元配列と定義 配列サイズは19行1列
ReDim delta_x_from_baseline(18, 0) '
Dim eventid As String, baselineid As String

    For i = 0 To 18 '行要素数は19、0からスタート
        eventid = "event" & Cells(i + 2, 1).Value
        baselineid = "baseline" & Cells(i + 2, 1).Value
        Set Rng_event = Range(Cells(2, 3), Cells(20, 3)).Find(eventid, LookAt:=xlWhole)
        Set Rng_baseline = Range(Cells(2, 4), Cells(20, 4)).Find(baselineid, LookAt:=xlWhole)
            delta_x_from_event(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_event.Row, 5).Value
            delta_x_from_baseline(i, 0) = Cells(i + 2, 5).Value - Cells(Rng_baseline.Row, 5).Value
    Next i
    Range(Cells(2, 6), Cells(20, 6)) = delta_x_from_event  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
    Range(Cells(2, 7), Cells(20, 7)) = delta_x_from_baseline  '19行1列の配列サイズと合わせたセル範囲に、配列を一括代入
End Sub

とすると、私が求めていた結果となりました。
1と10を誤認するため、Findメソッドでは注意が必要ということなのでしょうか。

本当にありがとうございます。

(アイス) 2020/05/16(土) 18:58


 こんばんは!
お邪魔します。。。
解決されて良かったです。
方法は、色々あると思いますので、、一例です。
まぁ、、気が向いたら参考にしてみて下さい。
オリジナルを変更しようとしたんですけど、、わかりませんでした。。。
なので、、私のコードも良かったら。。。でいいので、、、
あっ、、検索は一回なめると結構楽になります。。。
では、、では、、、

 Option Explicit
Sub てすと()
Dim MyA As Variant
Dim MyEve() As Variant
Dim MyBas() As Variant
Dim MyId As Variant
Dim i As Long
Dim n As Long
Dim k As Long
MyA = Range("A1").CurrentRegion.Resize(, 7).Value
ReDim MyEve(0)
ReDim MyBas(0)
For i = LBound(MyA, 1) + 1 To UBound(MyA, 1)
    If MyA(i, 3) <> "" Then
        MyEve(n) = MyA(i, 5)
        n = n + 1
        ReDim Preserve MyEve(n)
    End If
    If MyA(i, 4) <> "" Then
        MyBas(k) = MyA(i, 5)
        k = k + 1
        ReDim Preserve MyBas(k)
    End If
Next
k = 0
For i = LBound(MyA, 1) + 1 To UBound(MyA, 1)
    If MyId <> MyA(i, 1) Then
        MyId = MyA(i, 1)
        k = k + 1
    End If
    MyA(i, 6) = MyA(i, 5) - MyEve(k - 1)
    MyA(i, 7) = MyA(i, 5) - MyBas(k - 1)
Next
Range("M1").Resize(UBound(MyA, 1), UBound(MyA, 2)).Value = MyA
Erase MyA, MyEve, MyBas
End Sub
(SoulMan) 2020/05/16(土) 19:42

>ステップイン(F8)確認ですが、
>代入する際に配列で一気に代入するのでワークシート上では、
>なぜE14セルを参照してしまうのかわかりませんでした。

シート上の変化を観察するだけではありません。

VBE上で、コードの各変数にマウスカーソルを当てると
その時点で、変数に何が入ってるか確認できます。

あるいは、ローカルウインドウを使えるようになるとよいです。
(配列を使うなら特に)

http://www.asahi-net.or.jp/~ef2o-inue/vba_k/sub04_040_03.html

(マナ) 2020/05/16(土) 20:21


 参考に
 Sub Test()
    Dim myCol As Collection
    Dim c As Range, LastRow As Long, v As Variant, i As Long

    Set myCol = New Collection
    LastRow = Cells(Rows.Count, "A").End(xlUp).Row
    ReDim v(2 To LastRow, 1 To 2)
    For Each c In Range("C2:D" & LastRow)
        If c.Value <> "" Then myCol.Add Cells(c.Row, "E").Value, c.Value
    Next
    For i = 2 To LastRow
        v(i, 1) = Cells(i, "E").Value - myCol("event" & Cells(i, "A").Value)
        v(i, 2) = Cells(i, "E").Value - myCol("baseline" & Cells(i, "A").Value)
    Next i
    Range("F2:G" & LastRow).Value = v
 End Sub

(ピンク) 2020/05/16(土) 22:44


Soulman様 マナ様 ピンク様

ありがとうございます。
実務(エクセル使用でのデータ処理と医療統計解析)を進めながらVBA学習していますが、動的配列やコレクションオブジェクトなどご教授いただいたコードを咀嚼するのに時間がかかっています。しかし、教えていただいたこと一つ一つ学習してエッセンスを自分のものにしたいと思います。またその中でもマナ様のおっしゃるようにトラブルシューティングスキルとしてイミディエイト、ローカルウィンドウ、ウォッチリストなどデバッグ作業も出来るようになれればと思います。沢山の事教えていただき今日中に全て理解して返答することが難しいのでひとまずお礼を申し上げたく思います。

(アイス) 2020/05/16(土) 23:52


コメント返信:

[ 一覧(最新更新順) ]


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