VBA配列で高速化する方法は?

このQ&Aのポイント
  • Excel VBAでデータ処理を行っている際に、配列を使うことで処理の高速化が可能です。今回の処理では、対象行について毎回ループを回して列番号を調べていますが、1行目に●が入っている列番号を事前に配列に格納することで高速化が期待できます。
  • 1行目に●が入っている列番号を事前に配列に格納することで、ループ(2)の部分で配列を使用することができます。配列に格納された列番号のみを対象に処理を行うことで、処理時間を短縮できます。
  • 配列を使用することで、ループ(2)の処理を効率化することができます。1行目に●が入っている列番号を事前に配列に格納し、ループ(2)ではその配列を使用して処理を行うことができます。これにより、処理時間の短縮が期待できます。
回答を見る
  • ベストアンサー

VBA配列 印のついた列番号を格納して利用したい

Excel VBAで下記処理を作成しました。配列を使えば処理が高速化できるのでは?と思い調べたのですが、配列を使ったことが無く、挫折しましたのでどなたかご教示いただけませんでしょうか。 <対象データ>(添付画像の黄色いセルが処理対象) ・処理対象の行は「区分」列に「0」が入っている ・処理対象の列は、1行目に「●」が入っている <作成した処理> ・ループ(1):データ開始行(画像では3行目)から順に見ていく  ・IF:「区分」列が「0」なら下記に進む(0以外なら次の行へ)   ・ループ(2):列を左から順に見ていく    ・IF:その列の1行目が「●」なら処理を行う For i = 3 To 10000 If Cells(i , 1) = 0 And Cells(i , 1) <> "" Then その行に対する処理の下準備のコード(記載省略) For j = 2 To 500 If Cells(1 , j) = "●" Then Cells(i , j)に対する処理(記載省略) End If Next j End If Next i <問題点> データ量が数千行×数百列と相応にあるため、処理実行に数秒かかります。今後、処理をより複雑にしたいため、高速化できないかと考えています。 <質問> 対象行について毎回、ループ(2)で各列の1行目を調べて●だったら処理する、としているのが非効率かも知れないと思いました。 1行目に●が入っている列番号を調べるのは1度で良いので、それを配列に格納して、ループ(2)の部分で使うことはできますでしょうか? ご存じの方、ご教示いただけますと幸いです。よろしくお願いいたします。

質問者が選んだベストアンサー

  • ベストアンサー
  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.6

No5は間違って違うものをコピペしてしましました。無視してください。

tai1234
質問者

お礼

お陰様で当初やりたかったことは下記コードで達成できました。有難うございました! n = WorksheetFunction.CountIf(Range(Cells(1,1),Cells(1,500)), "●") ReDim mCol(n) '動的配列mCol()の要素数=●の数に再定義 For i = 1 To 500 If Cells(1, i) = "●" Then k = k + 1 '動的配列の0番目は使わない mCol(k) = i '動的配列のk番目に列番号を格納 End If Next i For i = 3 To 10000 If Cells(i , 1) = 0 And Cells(i , 1) <> "" Then その行に対する処理の下準備のコード(記載省略) For k = 1 To n Cells(i , mCol(k))に対する処理(記載省略) Next k End If Next i ※上記では簡略化のため、実際は変数で持っている部分も実数で書いています(セル範囲など) ※セル範囲を配列buf()に入れるという工夫も、今後試してみます。

その他の回答 (5)

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.5

No1を以下のように変更してもいいような気がします。 Sub Test() Dim buf As Variant Dim i As Long, j As Long Dim mCount As Long buf = Range("A1:F12") For j = 3 To 12 If buf(j, 1) = 0 Then 'その行に対する処理の下準備のコード For i = 1 To 6 If buf(1, i) = "●" Then Cells(j, i) = "処理" End If Next End If Next End Sub

tai1234
質問者

お礼

ありがとうございました

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.4

下準備のコードの存在を忘れてました。 For j = 3 To 12 If Cells(j, 1) = 0 And Cells(j, 1) <> "" Then 'その行に対する処理の下準備のコード(記載省略) For i = 1 To k Cells(j, mCol(i)).Value = "処理" Next i End If Next j

tai1234
質問者

お礼

ありがとうございました

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.3

> ・1行目を左から順に見て「●」が入っている列が【処理対象列】→ 結果(画像で「3・5・6列目」)を配列に格納 buf = Range("A1:F1") n = WorksheetFunction.CountIf(Range("A1:F1"), "●") ReDim mCol(n) For i = 1 To Range("A1:F1").Columns.Count If buf(1, i) = "●" Then k = k + 1 mCol(k) = i End If Next の部分です。mCol(k)に3・5・6が順に入ります。 列データを全て配列にいれたいのでしたら tmp=Cells(行数,mCol(k)) でtmpに入ります。 > ・ループ(1):データ開始行(画像では3行目)から順に見ていく >  ・IF:「区分」列が「0」なら下記に進む(0以外なら次の行へ) >    ・その行の【処理対象列】に対して処理を行う 行と列のループを入れ替えたらいかがですか For j = 3 To 12 If Cells(j, 1) = 0 And Cells(j, 1) <> "" Then For i = 1 To k Cells(j, mCol(i)).Value = "下処理" Next i End If Next j > 「If buf(j, 1) = 0」の判定を「For j = 3 To 12」の回数分繰り返すことになりますが、これを1回だけで済ませられないか すみません、「IF:「区分」列が「0」なら下記に進む(0以外なら次の行へ)」の処理を1回でというのがわかりません。

tai1234
質問者

お礼

ありがとうございました

tai1234
質問者

補足

ありがとうございます。bufとmColが配列なのですね。少しずつ理解してみて、やりたいことに適用できるか試してみます。

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.2

> 「各行の処理の下準備」ができなくなる buf(j, i) = "処理" の部分で処理すればいかがでしょう。 画像の場合だと下準備の回数は9回です。 極力セルにアクセスしないようにすると処理が早くなると思います。 > ループ(1)の前で●の列を調べた結果(画像例であれば「3・5・6列目」)を配列に格納し 一例です。 Sub Test2() Dim i As Long, j As Long, k As Long, n As Long, mCol As Variant Dim buf As Variant Dim mCount As Long buf = Range("A1:F1") n = WorksheetFunction.CountIf(Range("A1:F1"), "●") ReDim mCol(n) For i = 1 To Range("A1:F1").Columns.Count If buf(1, i) = "●" Then k = k + 1 mCol(k) = i End If Next For i = 1 To k For j = 3 To 12 If Cells(j, 1) = 0 And Cells(j, 1) <> "" Then Cells(j, mCol(i)).Value = "下処理" End If Next j Next i End Sub

tai1234
質問者

お礼

説明が下手ですみません。元の質問に沿った補足説明です。 <作成した処理> ・ループ(1):データ開始行(画像では3行目)から順に見ていく  ・IF:「区分」列が「0」なら下記に進む(0以外なら次の行へ)   ・ループ(2):列を左から順に見ていく    ・IF:その列の1行目が「●」なら処理を行う <こういう処理はできないか?> ・1行目を左から順に見て「●」が入っている列が【処理対象列】→ 結果(画像で「3・5・6列目」)を配列に格納 ・ループ(1):データ開始行(画像では3行目)から順に見ていく  ・IF:「区分」列が「0」なら下記に進む(0以外なら次の行へ)    ・その行の【処理対象列】に対して処理を行う 配列の使い方が解っていないので、後者はロジカルな書き方になっていないと思います。それとも、配列ではこういうことはできませんでしょうか。 作業対象のセル範囲を一旦配列に格納してからアクセスした方が高速になる、というのは解ります。それはそれで取り込みたいですが、1行目に●が入っている列は1回調べれば判っているのに、作業対象行で一々1行目を見に行くのは非効率ではないか、という観点です。 よろしくお願いいたします。

tai1234
質問者

補足

kkkkkm様 再度ご回答ありがとうございます。質問の<問題点>に記載しました通り、データは数千行ありますので、その方法だと下準備処理を数千回繰り返すことになります(画像はあくまで例示です) 一旦、元の質問も下処理云々も忘れていただいて、1回目のご回答で書いていただいた下記コードで再質問差しあげます。 下記では「If buf(j, 1) = 0」の判定を「For j = 3 To 12」の回数分繰り返すことになりますが、これを1回だけで済ませられないか、というのが元の質問の趣旨です。 (また行列の処理順を入れ替えるのはナシです。入れ替えると元の質問に戻ります。) Sub Test() Dim buf As Variant Dim i As Long, j As Long buf = Range("A1:F12") For i = 1 To 6 If buf(1, i) = "●" Then For j = 3 To 12 If buf(j, 1) = 0 Then '質問者メモ:ここを効率化したい Cells(j, i).Interior.Color = vbYellow 'buf(j, i) = "処理" End If Next End If Next

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.1

画像の状態のようにセルの色を黄色にする場合の一例です。 配列 buf(j, i)はbuf(行,列 )に対応します。 データの無いセルの値は0になります。 Sub Test() Dim buf As Variant Dim i As Long, j As Long buf = Range("A1:F12") For i = 1 To 6 If buf(1, i) = "●" Then For j = 3 To 12 If buf(j, 1) = 0 Then Cells(j, i).Interior.Color = vbYellow 'buf(j, i) = "処理" 'セルに値を代入する場合こちらで End If Next End If Next 'セルに値を代入する場合こちらで 'Range("a1:f12").Value = buf End Sub

tai1234
質問者

お礼

ありがとうございました

tai1234
質問者

補足

kkkkkm様 ご回答ありがとうございます。 しかしこれですと、区分列=0の全列について1行目=●を調べていたものを、1行目=●の全行について区分列=0ように、縦横の処理順を入れ替えただけで、かつ質問のコードに書きました「各行の処理の下準備」ができなくなる(する場合は膨大な繰り返しが発生する)と思います。 質問の書き方が悪かったのですが、ループ(1)はそのままにして、ループ(1)の前で●の列を調べた結果(画像例であれば「3・5・6列目」)を配列に格納し、ループ(2)ではその配列から取り出して処理対象セルを特定する、という動きはできませんでしょうか? よろしくお願いいたします。

関連するQ&A

  • エクセルVBAの配列について

    エクセルVBAの配列について VBAをはじめたばかりの初心者です。 現在、下記のようにデータを配列の中に入れ、 別シートに書き出そうとしております。 (配列へ読み込むところのみ) Dim 配列(1 To 件数, 1 To 9) As Variant For j =1 To 件数 For i = 2 To L If Cells(i, 2).Value = Tx_month Then For k = 3 To 11 配列(j, k - 2) = Cells(i, k).Value Next k End If Next j,i 現状では、データの最終行のみを「件数」分書き出してしまいます。 jとiのForが重なっているからだと思うのですが、どう書き直したら良いか分かりません。 質問をさせていただくのも初めてなので、分かりづらく恐縮ですが お力添え頂けますと幸いです。 どうぞ宜しくお願い致します。

  • (VBA) 配列の文字列を昇順で並べ替えたい

    タイトルの通り、配列に格納したファイル名を昇順に並べ替えたいのですが、期待通りに動作しません。 内部コード(ユニコード?)順には並んでいるようですが、エクスプローラの名前順と同等にはなりません。 どのようにしたら、配列のファイル名をエクスプローラと同じように並べ替えできますか? (テストに使用したコード) Public Sub Test1() Dim FileNames() As String Dim WSH As Object Dim MyPathName As String Dim MyFileName As String Dim i As Integer Dim j As Integer '処理対象フォルダを指定 Set WSH = CreateObject("WScript.Shell") MyPathName = WSH.SpecialFolders("MyDocuments") Set WSH = Nothing i = 0 MyFileName = Dir(MyPathName & "\" & "*.*") If MyFileName = "" Then MsgBox "対象ファイルが1つも見つかりません。", , "処理終了" Exit Sub End If 'ファイル一覧を配列に格納 Do Until MyFileName = "" i = i + 1 ReDim Preserve FileNames(1 To i) FileNames(i) = MyFileName MyFileName = Dir Loop '配列を並べ替える For i = 1 To UBound(FileNames) - 1 For j = i To UBound(FileNames) If FileNames(i) > FileNames(j) Then MyFileName = FileNames(i) FileNames(i) = FileNames(j) FileNames(j) = MyFileName End If Next j Next i End Sub ※以降の処理は、Excel で処理するか Access で処理するかまだ決めていません。  (投稿文字数の関係で詳細は省略)

  • カウンタを使用した配列の格納について

    EXCEL VBAについての質問です。 どなたか添削願えないでしょうか? A1からE40までSingle形式のデータが入っています。 ただし、A列はすべて見出し、行には5行おきに1,6,11...行目に見出しが入っています。 4x4のデータが8個ある計算です。 見出しを除くデータをすべて(8,4,4)の配列に格納しようと思い、下記のコードをSheet(1)に 書きましたが、「インデックスが有効範囲にありません」と出てうまく格納できません。 上の「Next j」を生かして、下の「Next j」を殺し、イミディエイトウインドウで確認したところ、 ネストの外ではうまくループしているようですが、入れ子にするとデータがうまく入りません。 jのループ完了後には  j=36  データ番号=8 が入っているはずですが、j がうまく格納されないようで、値がすべて0になります。 上の「Next j」と直下の2行をいかした場合、きちんと値が入り、うまく機能するようです。 色々考えましたが、手詰まりになってしまったので、何がおかしいのか教えてもらえません でしょうか?よろしくお願いします。 Option Base 1 Const データ数 As Integer = 8 Const 列数 As Integer = 4 Const 行数 As Integer = 4 Dim データ番号 As Integer Sub 換算値格納() ReDim 換算値(データ数, 行数, 列数) As Single For j = 1 To 36 Step 5 '(例)j=1→データ番号1、j=6→データ番号2、.... データ番号 = (j - 1) / 5 + 1 Debug.Print "データ番号=" & データ番号 & " j=" & j 'Next j 'データ番号 = 8'<任意に変えてみてください> 'j = (データ番号 - 1) * 5 + 1 For i = 4 To 19 '列定義 'カウンタを4で割って1を足したものが0になる (例)14÷4=3...2 → 2+1=3 列 = i Mod 4 + 1 '行定義 'カウンタがちょうど割り切れたら行をひとつ増やす (例)i=4,8,12...にて列=1,2,3 If i Mod 4 = 0 Then 行 = 行 + 1 換算値(データ番号, 行, 列) = Cells(行 + j, 列 + 1).Value Debug.Print "(" & データ番号 & "," & 行 & "," & 列 & ")は" & 換算値(データ番号, 行, 列) Next i Next j End Sub

  • VBAの配列について

    初めまして、VBAの配列の入力方法について質問させてください。 大量のデータの処理を高速化するため、配列を使用して以下のVBAを入力しました。 インターネットで調べ、見よう見まねで入力してみたものです…(T_T) 内容は、シート「資料」のC列とシート「Sheet1」のG列の文字列が同じ かつ、シート「資料」のL列から最終列(そのときによって変化します) とシート「Sheet1」のE列の文字列が同じ場合、 シート「資料」のA列~D列及びL列から最終列で文字列の一致したセルを 着色するというものです。 変数「アイス」と「チョコ」にそれぞれシート「資料」のデータと シート「Sheet1」のデータを格納したつもりなのですが、 実行したところ「配列がありません。」というエラーメッセージが 表示されました。 どうやらデータを配列として格納できていないときに表示される エラーメッセージのようなのですが、変数の型を変更してみたり、 配列をアイス(2)にしてみたりと、色々方法を変えて試してみたものの、 処理は成功しませんでした(T_T) 一体何が原因で処理が成功しないのか、どなたかご教授いただけると とても嬉しいです…!よろしくお願いいたします。 ちなみに、配列を使用しない場合の処理は、時間が15分ほどと かなりかかりますが、成功しています。 Application.ScreenUpdating = True Dim アイス, チョコ As Long Dim i As Integer, j As Integer, k As Integer アイス = Sheets("資料").Cells(Rows.Count, 1).End(xlUp).Row チョコ = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row For i = 3 To Sheets("資料").Cells(Rows.Count, 1).End(xlUp).Row For j = 12 To Sheets("資料").Cells(i, 12).End(xlToRight).Column For k = 2 To Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row If アイス(i, 3).Value = チョコ(k, 7).Value And アイス(i, j).Value = チョコ(k, 5).Value Then Sheets("資料").Range("A" & i & ":D" & i).Interior.ColorIndex = 22 アイス(i, j).Interior.ColorIndex = 22 End If Next k Next j Next i

  • VBAの配列の格納について

    エクセルのVBAで、セルのデータを配列に格納するスピードを向上したいと思います。 例えば、A1~A10000のセルにデータを書き出す場合、 For 行番号 = 1 To 10000 Cells(行番号, 1).Value = 1 Next よりも、一旦、配列に書き込んだ後、一気にセルに書き込んだ方法が早いのですが、 For 行番号 = 1 To 10000 HAIRETU(行番号, 1) = 1 Next Range("A1:A10000").Value = HAIRETU() 今度は、A1~A10000に書き込んだデータを、再度、配列に格納する場合、 For 行番号 = 1 To 10000 HAIRETU(行番号, 1) = Cells(行番号, 1).Value Next とすると時間がかかるので、 HAIRETU() = Range("A1:A10000").Value というような処理をしたいのですが、うまくいきません。 よい方法がありましたら、ご教授ください。 よろしくお願いします。

  • ExcelのVBAの配列に関する質問です。

    ExcelのVBAの配列に関する質問です。 sheet1のデータをsheet2に表示するVBAを作成しています。。 sheet1のデータは7行目からスタートし、sheet2のデータは26行目からスタートしています。。 sheet1とsheet2の列は同じ並びではないため、それぞれのシートの列番号をCellsを用いて指定しています。 Sub test1() Dim endrow As Long endrow = Worksheets("sheet1").Range("A65536").End(xlUp).Row Dim i As Long Dim j As Long For i = 7 To endrow j = i + 19 Worksheets("sheet2").Cells(j, 1) = Worksheets("sheet1").Cells(i, 2) Worksheets("sheet2").Cells(j, 8) = Worksheets("sheet1").Cells(i, 28) Worksheets("sheet2").Cells(j, 9) = Worksheets("sheet1").Cells(i, 31) Worksheets("sheet2").Cells(j, 10) = Worksheets("sheet1").Cells(i, 32) ・ ・ ・ ※長いので省略 Next i End Sub 上記のtest1は正常に動くのですが、データ量が多いため、処理に時間がかかってしまいます。 高速化できないかと、以下のように変更しました。 Sub test2() Dim dataRange1 As Variant Dim dataRange2 As Variant dataRange1 = Worksheets("sheet1").Range("A1:GI10006") dataRange2 = Worksheets("sheet2").Range("A1:DZ10018") Dim endrow As Long endrow = Worksheets("sheet1").Range("A65536").End(xlUp).Row Dim i As Long Dim j As Long For i = 7 To endrow j = i + 19 dataRange2(j, 1) = dataRange1(i, 2) dataRange2(j, 8) = dataRange1(i, 28) dataRange2(j, 9) = dataRange1(i, 31) dataRange2(j, 10) = dataRange1(i, 32) ・ ・ ・ ※長いので省略 Next i End Sub test2は、エラーメッセージ等は表示されませんが、sheet2にデータが表示されません。 ちなみに、以下のようにsheet1のみ配列化した場合は、正常に表示されました。 Worksheets("sheet2").Cells(j, 1) = dataRange1(i, 2) Worksheets("sheet2").Cells(j, 8) = dataRange1(i, 28) Worksheets("sheet2").Cells(j, 9) = dataRange1(i, 31) Worksheets("sheet2").Cells(j, 10) = dataRange1(i, 32) test2は、どこが間違っているのでしょうか? ご教示ください。 よろしくお願いいたします。

  • MATLAB 256列以上の配列格納について

    こんにちわ。 現在、matlabで行971、列971程度の配列を用いて画像化したいと考えています。 しかし、列が256を超えるとエラーが出てしまい、処理できません。 ソースは下記の通りです。 971×971の正方形の画面にするので、1列(971×971)行などのように分割に全部入れて処理もするわけには行かないと思います。 よってなんとか256以上の列を使っていかなければならないです。 どうか、わかる方がいればお願いいたします。 //////////////////////////////////////////////////////////////// clear all; j=971; jj=5; test=[1:j,1:j]; for k=0:jj test=load(sprintf('APW%04d.csv',k)');%kの値を%04dに上書き end for i=1:j test(:,i) = abs(test(:,i)/j);%データを0~1に補正 end /////////////////////////////////////////////////////////////// エラー内容は下記の通りです。 //////////////////////////////////////////////////////////////// ??? Attempted to access test(:,257); index out of bounds because size(test)=[971,256]. エラー ==> Copy_of_Gzousyori at 14 test(:,i) = abs(test(:,i)/j);データを0~1に補正 ////////////////////////////////////////////////////////////////

  • VB.NET 配列の格納

    VB.NET 配列の格納 エクセルから任意の列の値を格納したいのですが・・・ 現在、値の取得は以下のようなFor文ループを書いています。 各値を一時記憶(格納)したいのですが、どう書けばいいのでしょうか。 また、各列内で重複する値があった場合、重複を避けて値を取得したいのですが どう書いていいかわかりません。 初歩的な質問で申し訳ありませんが、宜しくお願いします。 PN()→G列の各セルの値の変数 JN()→H列の各セルの値の変数 の変数GYO→シートの最終行 j = 1 For i = 1 To GYO '【リストのG列から部品番号を取得】 PN(j) = xlSheet.Cells(i, 7).Value '【リストのH列から部品和名を取得】 JN(j) = xlSheet.Cells(i, 8).value Next

  • VBAでコントロール配列を持つことは可能ですか?

    Excel2002、OSはXPです。 VBではコントロールに配列を持つことが出来ましたが、 VBAでコントロール配列を持つことは可能でしょうか? やりたいことはユーザーフォームにラベルを100個ぐらい貼り付けて、 テキストボックス入力した値がCell(i,1)と等しければ、 i番目のラベルのCaptionに文字を入力したい。という感じです。 もしVBのようにコントロール配列を持てれば、 If TextBox1.text=Cells(i,1) Then Label(i).Caption="~~" という風に出来るのですが、どうも配列の設定がVBのようにできません。 プロパティにINDEXが無いですし、オブジェクト名を同じにしたら エラーとなってしまいますし。。 もし出来ないのであれば、何か回避策のようなものはないでしょうか? 力技で100個IFを書けば出来ることは出来るのですが・・ If TextBox1.text=Cells(i,1) Then If i = 1 Then Label1.Caption="~~" ElseIf i=2 then Label2.Caption="~~" ・・・・

  • C言語 文字列格納

    テキストファイルから整数データ又は文字列を読み込んで配列に格納する動作についての質問です。 テキストファイルが1行区切りの整数型なら1次元配列で for(i = 0; i < maxSize; i++) { fscanf(fp,"%d", &data[i]); } テキストファイルが1行区切りの文字列なら2次元配列で for(i = 0; i < MAXSIZE; i++) { if (fscanf(fp,"%s", &data[i][300]) == EOF) break; } for(j = 0; j < i; j++) printf("%s\n", data[j]); みたいな具合に格納できたんですが、 テキストファイルが1行区切りのデータではなく、空白文字区切りの文字データだった場合、それぞれどのようにして配列に格納すればいいかがわかりません。 イメージとしては、1文字目から見ていって空白が出ればそこで切って格納していくというかんじなのですが・・・ 質問の内容がわかりにくいかもしれませんが、是非教えてください。お願いします。

専門家に質問してみよう