• 締切済み

データーの変化点を高速に検索する方法

VB6からExcel2002へアクセスしています。 エクセルで列方向(例えばA列)へB,B,B,A,A,A,A,A,C,C,C,C,B,B,B,B... のように格納されているデーターがあります(3万~5万行)。 このデーター列から変化点を高速に検索する方法はありませんでしょうか。 現在は、For-Nextで1セルずつ比較しながら変化点を検索、抽出しているため、非常に時間がかかっています。 Findメソッドは調べた限り、できそうにありませんでした。 高速化できる方法をご存知の方いらっしゃいましたら、よろしくお願いします。

  • elec2
  • お礼率61% (105/171)

みんなの回答

  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.9

#7,8です。 専門家では無いので、想像の域を出ませんが、VBAで下記の様なコードを実行すると、当方の遅いマシンで、50000回の倍精度浮動小数点数の計算にかかる時間は7~8msecでした。Variant型にしても、数割遅くなる程度でした。VBA恐るべし。 一方ローカルウィンドウなどで、ExcelのRange型を見ると、呆れるほど沢山の、多階層のメンバーを有する複雑なObjectである事が分かります。Excelのセルに計算をさせるときにかかる時間のほとんどはObjectとのやりとりの部分なのではないでしょうか。と、いう事で、Objectへの代入を一括で行う事で、高速化が図れるのだと想像します。 専門家の方がご覧になっていたら、補足をお願いします。 Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Sub test() Dim i As Long Dim a As Double, b As Double, dbRet As Double Call timeBeginPeriod(1) Sleep 100 Debug.Print timeGetTime For i = 1 To 50000 a = 10000.1 b = 9999.123 dbRet = Abs(a - b) Next i Debug.Print timeGetTime Call timeEndPeriod(1) End Sub

elec2
質問者

お礼

なるほど。 色々と試してみるしかなさそうですね。 ありがとうございました。

  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.8

#7です。それではExcelだけでやる方法にトライ。 Sheet2のA1:B50001にデータをおきます。 1行目はA~C列に適当な見出しを付けます。例えばC1を「差」とします A2:A50001は連番、B2:B50001は目的のデータとします。 Sheet1にフィルタオプションの条件を記します。 A1に「差」、A2に「>=100」等と条件を記します。 次のマクロを実行すると、C列の前データとの差が100以上のデータが抽出されます。実行時間はトータル1秒前後でした。範囲をまとめて処理するのがミソです。 Sub test() Debug.Print Now Sheets("Sheet2").Range("c3:c50001").Formula = "=abs(RC[-1]-R[-1]C[-1])" '再計算させると悲惨な目に遭うので、式から値に置き換え Sheets("Sheet2").Range("c3:c50001").Value = Sheets("Sheet2").Range("c3:c50001").Value Debug.Print Now Range("A1:C50001").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _ Sheets("Sheet1").Range("A1:A2"), Unique:=False Debug.Print Now End Sub

elec2
質問者

お礼

たびたび、ありがとうございます。 やはり「差分検出の式(いわゆる、微分といったところでしょうか)」という方法が一番高速でしょうか。 一行ずつ比較する方法が時間がかかるのは理解できるのですが、 回答いただいたような「エクセルに計算させる」方法が「極端」に高速な理由を知りたいです。

  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.7

横道にずれますが、Excelのファイルで処理しないといけないんでしょうか。試しに50,000件のデータで、Access2000でやってみました。49,999レコード目に特異値を設けて試験しました。ちなみに、2.4GのCeleronで、メモリは256MB、Windows2000です。 1.Dlookupを使った重たいクエリ(1件毎に検索される?)約3分 こんなSQLです。 SELECT T_data.ID, T_data.data, Abs(DLookUp("data","T_data","ID=" & [ID]-1)-[data]) AS 差 FROM T_data WHERE (((Abs(DLookUp("data","T_data","ID=" & [ID]-1)-[data]))>100)); 2.VBA + ADOで、普通のBASICのやり方で差を求める場合、差の絶対値の算出に20秒、そのテーブルから一定以上の値を抽出するクエリは一瞬でした。 ご参考まで。

elec2
質問者

お礼

ありがとうございます。 私も、ACCESSが使えるといいと思っているのですが「諸般の事情」で、エクセルでやるしかないんです・・・

noname#102340
noname#102340
回答No.6

NO.3です。 セルに書き込みもしているなら、前回のコードのbをb(n,0)のように2次元配列で宣言、データ取得後に一度にセルに代入する。 objExcel.Range("B1:B" & Cstr(n)).Value = b

elec2
質問者

お礼

補足、ありがとうございます。

  • hotosys
  • ベストアンサー率67% (97/143)
回答No.5

No.2です。 よく考えたらNo.4の回答内の並べ替えの部分は不要なので、以下で試してください。 Sub sample() 'excel用定数設定 Const xlUp = -4162 Const xlNumbers = 1 Const xlCellTypeFormulas = -4123 ' Dim xl As Object Dim lastRow As Long Dim d As Variant Set xl = CreateObject("Excel.application") 'xl.Visible = True '表示する場合 xl.Workbooks.Open "c:\book1.xls" 'excelファイルを開く With xl.ActiveWorkbook.ActiveSheet '開いた時のシートに対して 'With xl.ActiveWorkbook.Sheets("Sheet1") 'シート名で指定する場合 .Columns("B").Clear '結果列クリア lastRow = .Range("A" & .Rows.Count).End(xlUp).Row 'A列の最終行を取得 .Range("B2:B" & lastRow).Formula = "=IF(A2<>A1,ROW(),"""")" '変化した行に行番号を表示する式を設定 .Columns("C").Clear '最終結果列クリア .Range("B2:B" & lastRow).SpecialCells(xlCellTypeFormulas, xlNumbers).Copy .Range("C1") 'B列の計算式の数値のみC列に lastRow = .Range("C" & .Rows.Count).End(xlUp).Row 'C列の最終行を取得 d = xl.WorksheetFunction.Transpose(.Range("C1:C" & lastRow)) 'C列の最大行までの値を1次元配列に取り込む xl.ActiveWorkbook.Close False 'ブックを保存せずに閉じる xl.Quit 'excel終了 Set xl = Nothing 'excel変数破棄 '結果表示 MsgBox LBound(d) '最小添え字(1になる) MsgBox UBound(d) '最大添え字 MsgBox d(LBound(d)) '最初の変化点 MsgBox d(UBound(d)) '最後の変化点 'もちろん途中も見れます End With End End Sub

elec2
質問者

お礼

訂正、ありがとうございます。

  • hotosys
  • ベストアンサー率67% (97/143)
回答No.4

No.2です。 vb6から操作するなら以下ではどうでしょうか? 結果は配列dに入りますが、excelの関数使う関係上、d(1)-d(ubound(d))に入ります。 Sub sample() 'excel用定数設定 Const xlUp = -4162 Const xlPasteValues = -4163 Const xlAscending = 1 Const xlNo = 2 Const xlCellTypeConstants = 2 Const xlNumbers = 1 ' Dim xl As Object Dim lastRow As Long Dim d As Variant Set xl = CreateObject("Excel.application") 'xl.Visible = True'表示する場合 xl.workbooks.open "c:\book1.xls" 'excelファイルを開く With xl.ActiveWorkbook.ActiveSheet '開いた時のシートに対して 'With xl.ActiveWorkbook.Sheets("Sheet1") 'シート名で指定する場合 .Columns("B").Clear '結果列クリア lastRow = .Range("A" & .Rows.Count).End(xlUp).Row 'A列の最終行を取得 .Range("B2:B" & lastRow).Formula = "=IF(A2<>A1,ROW(),"""")" '変化した行に行番号を表示する式を設定 .Range("B1").Resize(lastRow, 1).Copy '計算式を値にするためにコピー .Range("B1").Resize(lastRow, 1).PasteSpecial Paste:=xlPasteValues '値のみ貼り付け .Range("B1").Resize(lastRow, 1).Sort Key1:=.Range("B1"), Order1:=xlAscending, Header:=xlNo '並び替え(数値を前に移動して空白を後ろに移動) .Columns("C").Clear '最終結果列クリア .Range("B1").Resize(lastRow, 1).SpecialCells(xlCellTypeConstants, xlNumbers).Copy .Range("C1") '最初の結果列の値のセルのみ(""セルを除く)をC列にコピー lastRow = .Range("C" & .Rows.Count).End(xlUp).Row 'C列の最終行を取得 d = xl.WorksheetFunction.Transpose(.Range("C1:C" & lastRow)) 'C列の最大行までの値を1次元配列に取り込む xl.ActiveWorkbook.Close False 'ブックを保存せずに閉じる xl.quit 'excel終了 Set xl = Nothing 'excel変数破棄 '結果表示 MsgBox LBound(d) '最小添え字(1になる) MsgBox UBound(d) '最大添え字 MsgBox d(LBound(d)) '最初の変化点 MsgBox d(UBound(d)) '最後の変化点 End With End End Sub

elec2
質問者

お礼

なるほど。 こういうやり方もあるんですね。 参考にさせていただきます。 ありがとうございました。

noname#102340
noname#102340
回答No.3

VB6で動くか分かりませんが参考までに。 Dim a(,) As Object 'シートA列のデータ。Variant型? Dim b() As Integer '先頭位置を確保 Dim n As Integer 'データ数 n = 50 a = objExcel.Range("A1:A" & CStr(n)).Value 'objExcelは作成済みとする ReDim b(n) For i = 1 To n - 1 If a(i, 1) <> a(i + 1, 1) Then b(j) = i + 1 j = j + 1 End If Next ReDim Preserve b(j - 1)

elec2
質問者

お礼

ありがとうございます。 試してみたのですが、 現状方法:15分 提案方法:15分 とほとんど差はありませんでした。 何か妙案があればいいのですが。

  • hotosys
  • ベストアンサー率67% (97/143)
回答No.2

こんなのではどうでしょうか? B列に変化行を表示します。 Sub sample() Dim lastRow As Long lastRow = Range("A" & Rows.Count).End(xlUp).Row 'A列の最終行を取得 Columns("B").Clear '結果列クリア Range("B2:B" & Rows.Count).Formula = "=IF(A2<>A1,ROW(),"""")" '変化した行に行番号を表示する式を設定 Columns("B").Copy '計算式を値にするためにコピー Columns("B").PasteSpecial Paste:=xlPasteValues '値のみ貼り付け Columns("B").Sort Key1:=Range("B1"), Order1:=xlAscending, Header:=xlNo '並び替え(数値を前に移動して空白を後ろに移動) End Sub ただB列にゴミが残るので、邪魔のようならC列を使って掃除します。 Sub sample() Dim lastRow As Long lastRow = Range("A" & Rows.Count).End(xlUp).Row 'A列の最終行を取得 Columns("B").Clear '結果列クリア Range("B2:B" & Rows.Count).Formula = "=IF(A2<>A1,ROW(),"""")" '変化した行に行番号を表示する式を設定 Columns("B").Copy '計算式を値にするためにコピー Columns("B").PasteSpecial Paste:=xlPasteValues '値のみ貼り付け Columns("B").Sort Key1:=Range("B1"), Order1:=xlAscending, Header:=xlNo '並び替え(数値を前に移動して空白を後ろに移動) Columns("C").Clear '最終結果列クリア Columns("B").SpecialCells(xlCellTypeConstants, xlNumbers).Copy Range("C1") '最初の結果列の値のセルのみ(""セルを除く)をC列にコピー Columns("B").Delete '最初の結果列を削除 End Sub

elec2
質問者

お礼

なるほど。 先にエクセル上で変化点検出を行っておくのですね。 エクセルのシート上には他にも多量のデーターが存在しますので、 WorkSheetオブジェクトかCellsオブジェクトで同じことができないか試してみます。 ありがとうございました。

  • warumx
  • ベストアンサー率0% (0/9)
回答No.1

もっと具体的に説明しないと分かる人はいないでしょう。 >B,B,B,A,A,A,A,A,C,C,C,C,B,B,B,B... まず、A,B,C・・・等は数値ですか文字列ですか。 同じ文字は同一の内容が連続しているという意味ですか? 変化点の定義は何ですか?

elec2
質問者

お礼

>もっと具体的に説明しないと分かる人はいないでしょう。 決めつけるのもどうかと思います。 少なくともお2方からはアドバイスをいただけましたし、 自身も「なるほど、いけるかも」と思い、試してみました。 「万人にわかる説明」という言葉があります。 たしかに、この質問はそうではなかったかも知れません。 しかし、琴線にふれて的確な回答をいただけたことも事実です。 ありがとうございました。

elec2
質問者

補足

お世話になります。 >まず、A,B,C・・・等は数値ですか文字列ですか。 文字列もありますし、数値(整数)もあります。 >同じ文字は同一の内容が連続しているという意味ですか? はい、そのとおりです。不定の個数が連続しています。 >変化点の定義は何ですか? 同じデーターが連続している最後(または次の先頭)を検出したいです。

関連するQ&A

  • 【excelVBA】Findメソッドで検索対象を複数列

    findメソッドで、検索対象を複数列&検索条件を複数にしたいのですが、可能でしょうか? イメージ的には、下のようなデータが入っているシートから、 AとB列を条件にしてC列の同じ行の値を取得したいのです。 A |B |C 00|00|01 00|00|02 00|01|01 Range("A:B").Find(what:="00"&"00"・・・)のような感じです。 →期待される取得結果は01と02です。 今のところ方法がおもいつかないので、 (1)A列を条件にしB列 (2)(1)のB列を条件にしC列 と二段階で取得するしかないかな~と思っています。 このような処理は不可能でしょうか?また可能ならばその記述方法をご教授ください。よろしくお願いします。

  • データ抽出方法

    おはようございます。 sheet1 B列、C列に重複してるデータと、そうでないデータが混在して沢山あります。 sheet2 B列、C列に一点一様の型でデータを抽出したいご伝授下さい sheet1 B列、C列 A-1-1 A A-1-1 A B-2-1 D B-2-2 E C-2-1 B C-2-1 B C-2-3 C sheet2 B列、C列(抽出結果) A-1-1 A B-2-1 D B-2-2 E C-2-1 B C-2-3 C

  • 検索値に一致するデータ行の抽出方法(エクセル)

    例えば・・・ ↓データ _ABCDE 1あカa1122 2あキb1223 3いクc1324 4うケd1425 5うコe1526 のようなデータがあったとして、別のシートの任意のセル(例えばA1)に「あ」と入力し、C列に「カ」を入力することでC列以降のデータをD列以降に並べたいのですが・・・ 結果↓ _ABCDE 1う←任意のセル:データシートのA列の検索値 2 3__*項目* 4__ケd1425 5__コe1526 ___↑ ___データシートのB列を検索する値を入力するC列    *データの行数は5000は軽くあります。 *データシートは、受け取る側のシートとは別のファイルにしたい。 *受け取る側は、1~100行になります。(ファイルによります) *受け取る側のファイルは、100ファイルぐらいになります。 *いずれのファイルも列数は2,30列程度です。 *格納するフォルダは同じです。 *できればフォルダ毎別のPCに持っていっても作業できるようにしたい。 環境:構築していくマシンは、Windows2000Pro,Excel2000です。 (実際の抽出作業もこれがメインのPCですが、Win95、98,Excel97、2000でも抽出作業ができれば嬉しいです。)

  • エクセル、複数あるデータを順番にピックアップする方法

    すみません、教えてください。 下記のようなデータの中から、たとえばC行の中から”りんご”に当てはまるデータを検索し、その列のA行とB行を抽出したいとすると、どのような方法がありますでしょうか。 別の空のシートに抽出したいのですが、よくわかりません。 よろしくご教授ください。お願い致します。 【元データ】     列A   列B   列C   列D   … 列1  八百丸  渋谷区  りんご  100 列2  八百吉  港 区  みかん  80 列3  八百屋  新宿区  か き  100 列4  八百正  新宿区  りんご  200 【求めたい検索結果】 八百丸 渋谷区 八百正 新宿区

  • Excelデータをフォルダ内から検索

    Excelのデータ(*.xls)がたくさん格納されているフォルダがありまして、 その中から例えば「部品A」と記述があるファイルを抽出したいのですが、 Windowsの検索機能を用いて「含まれる文字列」のところに「部品A」などと 記述しても、Excelデータが何らかのコード化(暗号化や圧縮)を行っているらしく 検索にひっかかりません。 何か良い方法はないでしょうか。 なお、OSはWindows98で、検索したいデータはExcel95~2000のものが混在しています。 よろしくお願いします。

  • EXCEL で検索、更新したい

    EXCEL でわからない箇所があり質問します。 前提条件 Sheet1 A列:名前 B列:年月 C列:値 データは、名前と年月で一意になるデータシートがあり マスターデータとして全データが登録されている Sheet2 A列:名前 B列:年月 C列:値 ある条件でとあるデータベースから抽出した名前と年月の一覧がある この段階で値のセルには何も入っていない やりたい事 (1)この状態でマクロを実行し、A列、B列を条件に該当する行から    C列を抽出しSheet2 にセット (2)Sheet2のC列の値を変更後、マクロの実行で    A列、B列の条件を元該当する行から、Sheet1の C列に値を更新 (3)もし、Sheet2のA列、B列の条件に該当しない行がある場合、    Sheet1に行追加しC列を格納 という事をしたいと考えています。 Sheet1 の全ての行をマクロでループさせて A列、B列をif分で比較し行を取得し、Sheet2 に張る場合、 遅くなるのであまりやりたくないと思っています Find 関数も考えたのですが、ヘルプを見る限り、条件が1つしか指定でき ないように思えるのですが、複数列の条件を指定できる Find 関数 あるいは、類似の関数とあかがあるのなら教えてください 最悪は、EXCEL ADO で、自分自身をSQLで抽出、Update、Insert を行う 事も考えていますが、 EXCELのセル内の式で、INDEX関数やDGET関数で簡単に抽出できるのに (ただ、値を変更するので式はかけないけど・・・) わざわざ、ADOでプログラムを組むのもどうなんだろうと疑問に思ってます。

  • EXCEL・ 2つの列内にある文字データで一致するものを検索する方法

    始めまして 例えば 下記の様にA列内の文字データ(数字ですが実際には文字列) とB列内の文字データが同じものを検索する方法です。 C列に同じデータがあれば○と表示する方法でもいいです。 この場合123は検索対象となるので1行目のC列又は2行目のC列に 印か文字を表示させたいです。    A列 B列 1行 123 111 2行 456 123 3行 789 222 よろしくお願いします。

  • Excelで2列にまたがるデータを比較したい

    こんばんは。 A列とB列の比較に困っています(>_<) 例えば、  ======A===========|=========B==========|=======C======== 2 セパタクロー==========ペタンク==============  3 ペタンク==============カモギー==============    4 クリケット============ブズカシ==============    5 ブズカシ==============ホルヌッセン==========ホルヌッセン 6 コクボル==============ヤールギレッシュ======ヤールギレッシュ 7 ハーリング============クロッケー============クロッケー 8 カモギー==============タスポニー============タスポニー ・            ・             と、データが並んでおりまして、この中から「B列にはあるがA列にはないデータ」を、C列に抽出したいのです。 この場合「ホルヌッセン」「ヤールギレッシュ」「クロッケー」「タスポニー」が、B列にはあるがA列にはないデータなので、関数などを使ってC列にそのデータをそのまま抽出したいのですが、どの関数を使えばいいのか頭を悩ませています・・・。 B2「ペタンク」という文字列を、A列全てを検索範囲として検索し、もしA列に「ペタンク」という文字列があれば、C2には“空白”を返します。 もし、A列全てから検索しても「ペタンク」という文字列がなかった場合、C2にはそのまま「ペタンク」を返します。 それによって最終的に、「A列にはないがB列にはあるデータ」を、C列に抽出したいのです。 皆様のお力をお借しいただきたい次第です(>_<) よろしくお願いします<m(__)m>

  • エクセルで同じシート内でデーター検索方法

    エクセルで同じシート内でデーター検索方法 A1のセルからA1、A2、A3・・・と縦にに100以上のデータがあります。 B1にも同様にデーターがあります。 ある文字列の組み合わせが重複しているもののみを検索したいです。 検索結果として一番望ましいのは、別のセルに抽出ですが、対象となったセルに色づけでも良いです。 文字で表すのは難しいので、添付画像のようにしたいです。 説明下手でわかり難いかもしれませんがすみません。 Excel2000です WinXP HomeedtionSP3

  • Excelでのデータ検索関数について

    Excelでのデータ検索について悩んでいます。 例えば、A列では「あ~お」、B列では「A~H」でC列ではデータが入っている場合でA列の「い」、B列の「C」のデータを検索したいときはどのように関数を組み合わせれば良いでしょうか?色々試したのですがなかなかうまくいきません。vlookup、index関数で組み合わせてできるのでしょうか?やはりVBAでしか無理なのでしょうか? どなたかご存知の方、ご教示ください。

専門家に質問してみよう