[[20160830164232]] 『関数式のマクロ化』(T1608) ページの最後に飛ぶ

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

 

『関数式のマクロ化』(T1608)

お世話になります。
関数式のマクロ化を進めていて、下記の配列数式?で頓挫しています。

「AI2={=MIN(LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),{1,2,3}))}」

できるだけ一連の作業と統一性をもたせたいため勝手いいますが、
以下コードの「v24」(...)をご教授願えないでしょうか?

 Set r24 = Range("AI2:AI" & maxR)	'maxR;最終行
    v24 = r24.Value
    i = 0
    With WorksheetFunction
        For Each t In r24
            i = i + 1
            z24 = t.EntireRow.Range("AK1") 
	...
            v24(i, 1) = (...)
	...
        Next   
    End With
    r24.Value = v24

よろしくお願いいたします。

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


 数式はよくわかりませんけど、もしAI2に対する式が正しいとすればループ処理は不要で

    Range("AI2").FormulaArray = "=MIN(LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),{1,2,3}))"
    Range("AI2").AutoFill Destination:=Range("AI2:AI" & maxR), Type:=xlFillDefault

 この2行だけでいいのでは?

(β) 2016/08/30(火) 16:54


β様、 早々にありがとうございます。

説明不足でした、実は式はAI列以下にも入力してあります。(AI3=...=AK3...)
であれば、提示式をループで埋めればいいかとも思ったのですが、
目的が「高速化」にありまして、処理時間的にはどうなんでしょうか?
(トンチンカンな質問であれば、ご容赦ください..)
(T1608) 2016/08/30(火) 17:07


 本題じゃなくてすみません。

 この数式って、ちょっと変に感じるんですけど。。
   ↓
 >AI2=MIN(LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),{1,2,3}))

 「1,2,3」番目に大きい中で、一番小さいものだったら、単に3番目に大きいものなんじゃないですか?

 これとどこが違うんですか?
  ↓
 =LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),3)

(半平太) 2016/08/30(火) 17:26


半平太様、 ご指摘ありがとうございます。

まったくその通りで、式がまちがっていますね。
(おそらく同種の式の転用でまちがったのだと思います)
式を下記に訂正させていただきます。
{=LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),3)}

失礼しました。
(T1608) 2016/08/30(火) 17:58


 数式がよくわからないのですけど、もし、AI2 にセットする数式を AK列等、他のセルにフィルコピーできるものなら
 フィルコピーする領域を変更すればいいんですが?

 ちなみに、AK2 には、どんな数式が入るのですか?
 それと、実際にセットすべき領域は、どこですか?

 (セルには数式ではなく、計算結果を値でセットしたいということでしたか?)

(β) 2016/08/30(火) 18:02


重ね重ねスミマセン。
 
AK列(ID)、AY列(データ)共に数字が入力されますが、空欄もあります(バグ?)。
AI列(作業列)の式は、AK列のID別にAY列の3番目に大きい数字を「書き出す」というものです。
(「関数式のマクロ化」という表現が誤解を与えてしまいました)

よろしくお願いいたします。
(T1608) 2016/08/30(火) 18:33


 数式セットか計算結果の値セットかは別にして

 あらためて、どんなレイアウトで、その、どの部分にどんな計算結果をセットしたいか、
 明確に説明してもらえませんか。

(β) 2016/08/30(火) 18:37


 そもそもが

 (T1608) 2016/08/30(火) 17:07 のコメントは、私が提示した2行のコードで実行して、要件に合わないという判断をされたのですか?
 それとも、なにもせず、ただ、コードを見て、これはだめだと判断されたのですか?

(β) 2016/08/30(火) 18:42


β様、 ありがとうございます。また、説明不足すみません。

提示いただいたコードでの結果に問題なきことは確認済みです。
今回の結果を得るのを、マクロ処理で..というのが希望になります。
実データの抜粋を以下に示します。

 AI     AK		AY
 97	07030808	86
 97	07030808	82
 82	07030707	73
 82	07030707	45
 82	07030707	5
 82	07030707	64
 80	03020802	78

稀にですが、AK列が空欄の時があるので、その時はAI該当列も空欄に
したいです。 今検討中のイメージは以下なんですが...

 If IsEmpty(z24) Then
 	v24(i, 1) = ""
 Else
 	v24(i, 1) = ???

よろしくお願いいたします。
(T1608) 2016/08/31(水) 09:15


 横から失礼。
 空白セルの対処ならIF文を入れればいいのでは?

 Range("AI2").FormulaArray = "=IF(AK2="""","""",LARGE(IF($AK$2:$AK$6000=AK2,$AY$2:$AY$6000,),3))"
(bi) 2016/08/31(水) 09:27

一応下記の追加コードで関数式と同じ解を得たのですが...
処理が遅くて使い物になりません。(涙)

 If IsEmpty(z24) Then
 	v24(i, 1) = ""
 Else
        vv = r24.Value
 	For j = 1 To maxR
	        If Cells(j + 1, "AK").Value = z24 Then
                      vv(j, 1) = Cells(j + 1, "AY").Value
                End If
        Next
        v24(i, 1) = .Large(vv, 3)
 End If

まぁループ処理なので当然なのかも知れませんが、他の方法が思いつきません。
関数式より処理が早いマクロにしたいのですが..ご助言願えないでしょうか?
(T1608) 2016/08/31(水) 16:07
補足)
現在あるシートの処理速度の改善に取り組んでいます。
シートにはデータ列以外に作業列が20列ほどあり、各々に関数式が
セットされています。
これまでに本サイトへの投稿や他の投稿を参考にして、14列の関数式を
マクロ化して効果をあげていますが、今回の配列数式?で頓挫しています。
(今回うまくいけば、他への展開を考えています)
データ処理作業の効率上、常に「手動計算」に設定し、データ入力後に
「再計算」ボタンを押すようにしています。
したがって、マクロでの計算式セットでは処理時間の短縮にはあまり
貢献しないのではないかと考えています。
(β様の提示コードでの手動実測比較でもほとんど変わりませんでした)

よろしくお願いいたします。


 >関数式より処理が早いマクロにしたいのですが..ご助言願えないでしょうか? 

 いろいろ手はありますよ。
 なので、具体的に何をしたいか、その要件を教えてもらえませんか?
 数式を見て判断しろ ということかもしれませんが、βは 数式が超苦手で、そこで何をやっているのか
 誤解する可能性大ですので。

 さらに、なんとなく 同じコード内での3番目の値を表示するように思うのですが、もし 3番目がなければ
 (極端にいえば1番目しかなければ)どうするのかな? とか、
 10,10,10,7,7,5 とあった場合、1番目は 10 ですかね。3番目は? 5って6番目だという人もいるので。

(β) 2016/08/31(水) 17:07


本シートを作成してから2年近く経過していますが、今さらながらですが
提示式には不備があることがわかりました。
(実際には現在まで不具合発生には気づいていませんが..)

その修正を兼ねて改めて要件を記述しますが、お許し願えますでしょうか?
(要件)
・AK列、AY列は入力データです →AK列;識別ID、 AY列;固有データ(数値)
・AI列(作業列)に以下を出力
 1)AK列の同じ識別ID内のAY列の「大きい順から3番目」の数値を出力する
 2)下記の場合には「""」を出力
  イ.AK列が8桁の識別ID以外の時
  ロ.AY列が数値以外の時
  ハ.今回の要件の想定外の場合
 3)「大きい順から3番目」の定義
  イ.同値を含む(10,10,10,7,7,5 とあった場合、10が該当)
  ロ.3番目がない場合は2番目が該当、2番目なければ1番目が該当

以上です。

(T1608) 2016/08/31(水) 22:55


 書いて、エラーなく動くという確認しかしていません。
 不具合がある、あるいは、処理時間が長い ということであれば指摘願います。

 なお、【AK列が8桁の識別ID以外の時】ということですが、アップされたサンプルでは7桁ですし、
 別途、識別IDが、どこかにテーブルとして存在するのかどうかもわかりませんでしたので、空白以外は処理対象にしています。

 ★8:10 コード案、見直しておかしなところがあったので、いったん削除します。

 ★あらためて再掲します。

 Sub Sample()
    Dim al As Object
    Dim dicA As Object
    Dim dicD As Object
    Dim c As Range
    Dim v As Variant
    Dim i As Long
    Dim mx As Long
    Dim id As Variant
    Dim num As Variant
    Dim w As Variant

    Set dicA = CreateObject("Scripting.Dictionary")
    Set dicD = CreateObject("Scripting.Dictionary")

    mx = Range("AY" & Rows.Count).End(xlUp).Row
    ReDim v(2 To mx, 1 To 1)

    For i = 2 To mx
        id = Range("AK" & i).Value
        num = Range("AY" & i).Value
        If id <> "" Then
            If IsNumeric(num) Then
                If Not dicD.exists(id) Then Set dicD(id) = CreateObject("System.Collections.ArrayList")
                dicD(id).Add num
            End If
        End If
    Next

    For i = 2 To mx
        id = Range("AK" & i).Value
        If dicA.exists(id) Then
            v(i, 1) = dicA(id)
        Else
            If dicD.exists(id) Then
                dicD(id).Sort
                dicD(id).Reverse
                w = dicD(id).toarray
                num = w(WorksheetFunction.Min(UBound(w), 2))
                v(i, 1) = num
                dicA(id) = num
            End If
        End If
    Next

    Range("AI2:AI" & mx).Value = v

 End Sub

(β) 2016/09/01(木) 08:07


β様、 ありがとうございます!
全くストレスなく結果が出ます。(“あっ”という間です)

「Dic..」ですか..これが処理時間短縮の“キモ”なのでしょうか?
あるいは「Sort」?..
(時間短縮の“考え方”をいただけると嬉しいのですが)
「Dic..」は頭には浮かびましたが実現する能力がなく、手も足も出ません。
今までマクロ化したものでも未だ20秒〜30秒要しているのがあり応用したいのですが、
とてもとても..
少しづつでも前進せねば..とは思いますが、壁にぶつかった時はまた手(頭?)を
お借りしたいと思います。

これからも、よろしくお願いいたします。

(T1608) 2016/09/01(木) 11:22


 コードではDictionaryも使っていますが、脇役の扱いで、今回の主役はArrayListです。

 Dictionary、SortedList、ArrayList 等々、VBAからも利用が可能なんですが
 それらの実装が優れているんでしょうね、難しいことを考えずにコードを書くだけで
 (たとえ、そのコードが下手なものであっても)比較的優良な処理効率が得られます。

 これらの【入れもの】には、特性というか、得意分野があって

 ・Dictionary は、登録したキーの順番にはかかわらず、キーを与えて直接参照、書き込みができます。
 ・SortedListは、ほぼ Dictionaryと同じ機能ですが、加えてキーを書きこんだ時に、自動的に中身を【昇順】にしてくれます。

 これら2つの難点というか、同じ値をキーとして複数のレコードを格納することができません。
 (工夫すればできるんですが、ややこしくなります)

 ・ArrayList は、キーとデータ という関係ではなく データしか登録できません。ただし
  同値のものを複数格納することができます。
  格納したものを昇順に並び替えることができますし、昇順で並び替えた後、降順にすることもできます。

 で、今回のコードは 識別ID ごとに ArrayList を持たせ(DicD(識別ID)がその識別IDに紐付いたArrayListになっています)
 固有データ(数値)を、その 識別IDに紐付いたArrayList に格納していきます。

 格納が終われば、そのArrayList内を降順(大きなものからの順)に並び替え、3番目に位置する値(1つ、ないしは2つしかない場合はそれ)
 を取得しています。同じ識別IDが何回も登場しますので、そのたびにこの処理を行うのは、もったいないので、別途、dicA に 識別コードをキーに、
 答えの数値をデータにして登録しておき、その登録があれば、dicAから直接抽出します。

 で、この結果をセル毎に書きださず、配列におさめておいて、最後に一挙に AI列に書きこんでいます。

 まぁ、あまりあせらず、一歩ずつ吸収していってください。

(β) 2016/09/01(木) 12:04


β様、 再々ご教授ありがとうございます。

このように解説いただけると、各々の本質的な機能(特性?)が
わかった気になるので困ったもんです。(苦笑)

「わかった=コードを書ける」までには遠い道程ですが、少しづつ..

ご提示のコード理解はこれからですが、躓いた場合は新たにUPさせて
いただきたいと思います。

これからも、よろしくお願いいたします。
(T1608) 2016/09/01(木) 14:13


コメント返信:

[ 一覧(最新更新順) ]


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