いつもお世話になっております。
現在大量にセルの内容を参照し、照らし合わせるマクロを作っていますが、内容が膨大でマクロでも
時間がかかるので、何とか高速化できないか、考えております。
そこで、セルを直接参照するより、配列を使った方が速くなるという話を聞いたのですが、
配列を使うときに普通はVariantで宣言をして、セルの値のみ格納すると思います。
例えば、次のように、A列とB列を10000個のセルを1個ずつ比較し、一致した時にある作業をさせて
C列に出力するとします。
A = Range("A1:A10000")
B = Range("B1:B10000")
For i = 1 To 10000
For j = 1 To 10000
If A(i, 1) = B(j, 1) Then
A(i, 1) = A(i, 1) + 1
End If
Next
Next
Range("C1:C10000") = A
このようなマクロならいいのですが、値が一致した時にそのセルの色を変えるだとか削除するだとか行うと、次のようになり、配列のみでの構成ができなくなってしまいます
A = Range("A1:A10000")
B = Range("B1:B10000")
For i = 1 To 10000
For j = 1 To 10000
If A(i, 1) = B(j, 1) Then
Cells(i, 1).Interior.ColorIndex = 3
End If
Next
Next
前者は15秒、後者は19秒かかりました。上のプログラムは例であり、
実際に実務の方でマクロを実行すると数十分かかってしまいます。
何とか、条件によってセルの色変えたり削除するときに、速度を低下させずに配列のみで構成することは不可能でしょうか?
No.4ベストアンサー
- 回答日時:
こんにちは。
#3、clです。#3の訂正が1点、
補足が2点、
お礼欄でのお尋ねに関するこたえを最後に書きます。
訂正■
> a = Range("A1:B" & nBtmRow).Value
> ReDim b(1 To UBound(a), 1 To 1)
> For i = 1 To nBtmRow
↑これ間違えました。正しくは↓
a = Range("A1:B" & nBtmRow).Value
ub = UBound(a)
ReDim b(1 To ub, 1 To 1)
For i = 1 To ub
たまたま Range("A1:... のように1行目から始まる範囲だから通ってしまいますが
実際は、配列の中身(インデックス)と行番地は相対ですから
あらためて UBound() 採らないとおかしいですね。失礼しました。
ちなみに、ub = UBound(a) ですが、これ a は2次元配列ですから、
省略しないで ub = UBound(a, 1) と書く方がベターでした。
(※ubはLong型です)
補足1■
> のように、ひとつずつ、処理するよりは
> Range("A2,A4,A6,A8,A10").Interior.ColorIndex = 3
> のように、プロパティ設定を一度で済ませた方が
合わせて書かなければいけなかったのですが、
「オブジェクトの参照・取得とプロパティ設定を一度で済ませた方が」
です。
要は"時間が掛かる処理"はなるべく減らそうということなので片手落ちはまずかったですね。
補足2■
> 配列を使って、作業セルに作業用のフラグ配列を出力して
提示したサンプルコードでいうとフラグ配列とここで呼んでいるのは
[True | Empty]です。フラグ配列というと普通は[True | Flase]ですね。
でもExcelの .SpecialCells() にとって、フラグと呼べるのは
"セルの値のデータ型の相違"ですから、
[Boolean | Empty]という二値をVariant型の変数に格納した後で配列ごとセル範囲に出力している
と、ちゃんと説明するべきでした。
これが理由で Variant型 の変数配列を使うのですし、
逆に Variant型 でなければできないことをしている訳です。
#3へのコメントへのレス■
> 1つ気になったのが、rangeオブジェクトでまとめて選択する方法として
> Joinの例をあげましたが、Unionを使わないのはやはり時間がかかるからですか?
端的にいうと「はい」その通りです。
少なくとも、
Set rng = Union(rng, rng2)
のように、ひとつずつ追加していくのは、割と時間掛かります。
とはいっても、Unionしないで、ひとつずつプロパティ設定をするよりは速いですから
処理を速くする方法のバリエーションのひとつに加えておいても良いと思います。
ただ、Union メソッド は、Range型の引数30個まで指定できますから、
私のスタイル(つまり好み)として、ひとつずつ追加していくことは全くやらないだけです。
加えて言うと、例えば
Union(Range("A2"), Range("A4"))
なんて、普通の人でもばかばかしくて書かないですよね。
Range("A2,A4") (←↑厳密には少し意味が違いますが。)
の方がはるかに速いですし。
で、突き詰めていくと、(少し私の話になってしまいますが)
Range() に指定できる参照文字列は255文字まで目いっぱい使って
Union() に指定できるRange型の引数は30個まで目いっぱい使って
30個ずつまとめたものを更にUnionして
Areas32767を超えないように手当てして、なんて、やっていくと
配列を引数にしてRangeオブジェクトを返す関数が出来て、
.SpecialCells とかExcelの一般機能使うより数倍速くすることは
可能だということを確かめたりもしました。
経験上、面倒だったのは、いつも数字の壁(仕様上の制限)だったので、
考えないで済むのなら、なるべく有利な環境に限定したものを
書いていった方が、取っ掛かりとしてはやり易いと思います。
(#この↑3行で済む話を長々書いた感もありますが(汗)
もし興味があれば↓
For Nextマクロの高速化についてご教示ください。
http://oshiete.goo.ne.jp/qa/4007086.html
私の"ガラパゴスなVBA"の話よりも他の方のが参考になるかもしれません。
あ、↑の質問者さんも"高速化"探究し続けていた方なので色々ヒントとか
共通の疑問とか、見つけやすいかも知れませんね。
それでは。。。
No.3
- 回答日時:
こんにちは。
お邪魔します。"配列のみで"というのはちょっと汲み取れませんけれど、
"配列を有効に使う"ことで処理を速くしたいってことなら色々方法はあると思います。
コレクションのプロパティ値を直接、配列で設定できるのは、
Rangeオブジェクトに関して言えば、単矩形範囲(ひとつの連続した四角い範囲)に対する
.Value プロパティと .Formula プロパティ、つまり、セルの値に関するもの、だけなので、
配列だけでどうにかするというよりは、Excelの一般機能の仕様に照らして
処理に掛かる時間を、より短くできる手段をまず選び、
その処理に配列を有効に使う方法はないか?というトライになるかと思います。
例えば、
For i = 2 To 10 Step 2
Cells(i, 1).Interior.ColorIndex = 3
Next i
のように、ひとつずつ、処理するよりは
Range("A2,A4,A6,A8,A10").Interior.ColorIndex = 3
のように、プロパティ設定を一度で済ませた方が
処理するセルが多いほど時間の差は大きくなります。
つまり目標とするべきは、沢山のAreasを持つRangeオブジェクトをどうやってひとつにまとめて取得するか
という点になります。
その意味では、"配列を有効に使える"方法が幾つかあります。
極端な例でいえば、(難度↓激高ですが)
Range("A2,A4,A6,A8,A10")というように参照文字列を作る過程で
ref = Join(Array("A2", "A4", "A6", "A8", "A10"), ",")
のように配列を使うことも考えられなくはないですよね(Array関数は配列を示す為の喩です)。
でも、
Join関数はExcel2000以降だし、Rangeの参照文字列はいつでも255文字まで、
RangeのAreasの上限は(昔調べただけなので最新バージョンは知りませんが)Excel2000で32767まで、
後述しますが、Range.SpecialCellsで取得できるRange.Areasの上限はExcel2003で8192まで、
(忘れてしまったまま、まだ調べてませんが最新バージョンはもっと多いですね↑)
Excelの仕様、バージョンごとの仕様、そういう具体的なひとつずつの条件
(というより"Excelの都合"?)に合わせる形で、トライの仕方が全く異なってきます。
なので、条件が曖昧なままでは切り口が見つけにくいでしょうから、
職場でExcel2003で使う人がいるとかいないとかバージョン情報や、
システム上、レコード数の上限が幾つに設定されているとか、
ひとつひとつ条件を確認してクリアにしていく所から始めた方がいいかも知れませんね。
.Sort .AutoFilter .AdvancedFilter .SpecialCells とか、
配列でフラグを出力した上で篩に掛ける、という方法には、使えそうなExcel一般機能
幾つかありますよね。
でもバージョンによっては量的な制限で足りない機能は使えないとか、あてにならないとか、
制限内で収まる保証があるならから、より速い手段を選べるとか。
こんな話、面白くないかも知れませんが、速く処理する為には、皆さんそうだと思いますけど
まめに情報収集して、細かな差異に拘り見逃さない態度で何度も挑戦して失敗して、、、
細かいこと、大切にして、続ける、体力、みたいのが、結局、物いうみたいです。
本題に戻って、
ひとつ例を挙げておきます。
「A列とB列の値を比較して、同じであれば、その行を赤く塗りつぶします」
10000行なら、バージョンの違いで動かないってことはない(Excel2000以降)ので、
配列を使って、作業セルに作業用のフラグ配列を出力して
.SpecialCells()でフラグがたったセル範囲を纏めて取得して
.Interior.Colorを一度で設定します。
Win7/Excel2010で試したら40000行で0.3~0.5sec.程度でした。
勿論これが最善って訳じゃないですし、どんな方法が適しているかは質問者さんにしか判断できないです。
ほんの一例ですが、配列を使うメリットは出せていると思います。
Sub 準備()
Const NROW = 10000 ' ←行数を指定
With Range("A1:B" & NROW)
.Formula = "=RANDBETWEEN(1,5)"
.Value = .Value
End With
End Sub
Sub Re7727219j() 'okg
Dim a, b
Dim nBtmRow As Long
Dim i As Long, j As Long
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
nBtmRow = Cells(65536, 1).End(xlUp).Row
a = Range("A1:B" & nBtmRow).Value
ReDim b(1 To UBound(a), 1 To 1)
For i = 1 To nBtmRow
If a(i, 1) = a(i, 2) Then b(i, 1) = True 'a(i, 1) + 1 なら↓xlLogicalをxlNumbersに
Next
Application.ScreenUpdating = False
With Range("C1:C" & nBtmRow)
.Value = b
.SpecialCells(xlCellTypeConstants, xlLogical).EntireRow.Interior.Color = vbRed
.Value = Empty
End With
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
とても参考になりました。ありがとうございます。
>>配列を使って、作業セルに作業用のフラグ配列を出力して
.SpecialCells()でフラグがたったセル範囲を纏めて取得して
.Interior.Colorを一度で設定します。
この方法は試せそうなのでやってみます。
しかし、8192というのはちょっと少ないですね。2010の方もあるのでそちらで実行してみるのも手ですけど
1つ気になったのが、rangeオブジェクトでまとめて選択する方法として
Joinの例をあげましたが、Unionを使わないのはやはり時間がかかるからですか?
No.2
- 回答日時:
上記の例だと一致してもなお無駄に10000までチェックを続けていますが
実際のものはただしく書かれているのでしょうか。
上記が実際に即した考え方のものであるなら10000個の値について10000列(行)の比較をすべて行っていることになりますが・・・・
この回答への補足
すみません。実際にはきちんと工夫はしていますのでさすがに10000×10000ではないです。ただ、配列は使っていない状態だったので、結構時間がかかってしまってはいます。
そこで配列を使おうと思ったのですが、完璧にfor文の中身を配列だけにできないか疑問に思い質問させていただきました
そうですね。1~5万データあるファイルが10ファイルあって、比較対象がまた同じくらいありますね。
その2セットのファイルを比較するマクロを組んでます。
従って、高速化する方法を一つでも多く知っておきたいのです
No.1
- 回答日時:
>セルを直接参照するより、配列を使った方が速くなるという話を聞いたのですが、
理由は単純です。配列はメモリ上に取られるので早いのです。
>For i = 1 To 10000
>For j = 1 To 10000
1万回の繰り返しの中で1万回繰り返したら、1億回の繰り返しですよね。どうしても必要なら仕方ないですけど、回数は少なくしましょう。ちょっと知恵を絞ればできます。
例えば、ソートしておいてから比較すればかなり少なくなります。
あるいは合わせて2万個を1つの配列にしてソートすれば前後だけで一致をチックできるのでものずごく回数が減りますよね。
あーすみません。それはすでにしています。
上のは配列に関することのみに要点を置いてるのでそこは端折りました。
(excelのソートの規則性がいまいちわからず四苦八苦してますが)
私は上記プログラム純粋に配列のみで可能かどうかを聞きたいです。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) ファイル全てを .xlsm に変更したところ、プログラムが途中で落ちてしまっています 17 2022/12/07 12:03
- Visual Basic(VBA) まとめシートから集計シートへA列のコードが一致したら1行コピーするマクロをネット上で見つけました。こ 1 2022/08/30 14:11
- Visual Basic(VBA) ExcelVBAのマクロについて。 9 2022/05/04 14:50
- Excel(エクセル) VBAで組み合わせ算出やCOUNTIFSの処理を高速化したいです。 4 2022/04/07 02:38
- Visual Basic(VBA) マクロ実行時、自動で背景色を変えたい。 C列にあるチェックボックスをチェックするとB列に「TRUE」 4 2022/11/08 11:14
- Visual Basic(VBA) excel2021で実行できないマクロ。どこを直したらいいのか 2 2022/03/28 03:40
- Visual Basic(VBA) Sheet2からオートフィルターで売上日を抽出した件数をカウントし、その件数をSheet1のセルB1 2 2023/01/12 12:24
- Visual Basic(VBA) エクセルのマクロで対象ごとにシート分けしてその内容をセルに書き込みたい 9 2022/08/24 13:23
- Visual Basic(VBA) 列と行の名前(重複あり)が交差するセルに、データを入力したい 2 2022/06/25 22:42
- Visual Basic(VBA) excel VBA if文について 3 2022/03/27 17:42
このQ&Aを見た人はこんなQ&Aも見ています
-
性格の違いは生まれた順番で決まる?長男長女・中間子・末っ子・一人っ子の性格の傾向
同じ環境で生まれ育っても、生まれ順で性格は違うものなのだろうか。家庭教育研究家の田宮由美さんに教えてもらった。
-
Excelのセルの色指定をVBAから配列を用いて効率的に行う方法はあり
Visual Basic(VBA)
-
For Nextマクロの高速化についてご教示ください。
Excel(エクセル)
-
VBA/Worksheet_Changeがうまくいかない
PowerPoint(パワーポイント)
-
-
4
[VBA]csvファイルを開いて保存(高速化)
Excel(エクセル)
-
5
ユーザーフォーム スクロールバー 非表示にしたい
Excel(エクセル)
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VBA 変数名に変数を使用したい。
-
VB.NETの配列にExcelから読み込...
-
エクセルでXY座標に並べられた...
-
Redim とEraseの違いは?
-
配列の中の最大値とそのインデ...
-
テキストボックの文字を一行ず...
-
VB6のメモリ解放に関して
-
C#でbyte配列から画像を表示さ...
-
Excel2010のinputboxで複数デー...
-
構造体配列内の文字列検索のよ...
-
vba フィルター 複数条件 3つ以...
-
free()関数の多用 と Segment...
-
2次元配列のソート
-
VBAのワークシート関数で配列の...
-
Dir関数で読み取り順を操作でき...
-
ASPで配列を作る方法
-
excel vbaの配列なんですが・・・
-
配列の中から最大値だけ取り出...
-
大量の変数を定義するにはどう...
-
グラフの「項目軸ラベルに使用...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBA 変数名に変数を使用したい。
-
vba フィルター 複数条件 3つ以...
-
C#でbyte配列から画像を表示さ...
-
Excel2010のinputboxで複数デー...
-
エクセルでXY座標に並べられた...
-
構造体配列の特定のメンバーをF...
-
定数配列の書き方
-
コンボボックスのインデックス...
-
OutOfMemoryExceptionの回避策...
-
Dir関数で読み取り順を操作でき...
-
CheckBoxの配列化
-
構造体配列内の文字列検索のよ...
-
COBOLの基本的な事なので...
-
Redim とEraseの違いは?
-
VBAで配列引数を値渡しできない...
-
2次元配列の初期値
-
配列の中の最大値とそのインデ...
-
大量の変数を定義するにはどう...
-
VB6からの移行したいけど、VB.N...
-
VB6のメモリ解放に関して
おすすめ情報