[[20221219111418]] 『重複区間』(エビアン) ページの最後に飛ぶ

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

 

『重複区間』(エビアン)

以下のようにA列に開始値、B列に終了値があります。
値は0〜1000の整数値となります。
開始〜終了で重複した区間をC,D列に抽出したいです。
関数で可能なら、教えてください。

       A     B     C         D
 1   開始  終了  重複開始  重複終了
 2    600   689       660       689
 3    660   719       735       779
 4    665   669                    
 5    720   779                    
 6    750   869                    
 7    735   764                    

< 使用 Excel:Microsoft365、使用 OS:Windows10 >


わからん。条件が謎。

(通りすがり) 2022/12/19(月) 11:32:30


2行目のC、Dは、5行目のC、Dの間違いではありませんか?
(MK) 2022/12/19(月) 11:47:52

A2からB7の全てを、開始から終了の1本の線で描いた時に、列の表だと
計6本の線になります。
オーバーラップする範囲全てをCD列に出力したいのです。
(エビアン) 2022/12/19(月) 12:08:33


 >2行目のC、Dは、5行目のC、Dの間違いではありませんか? 

 行の見間違い。

 3行目のC、Dは、5行目のC、Dの間違いではありませんか? 
 でした。
(MK) 2022/12/19(月) 13:11:06

 やることの複雑さ考えると関数では無理なんじゃないかと。
 マクロならなんとか。
(´・ω・`) 2022/12/19(月) 13:27:09

 考え方的にはこんな感じでしょか?
    Sub test()
        Dim r As Range, u As Range
        Union([A600:A689], [B660:B719], [C665:C669], [D720:D779], [E750:E869], [F735:F764]).Value = 1
        With [G600:G869]
            .FormulaR1C1 = "=SUM(RC1:RC6)"
            For Each r In .Rows
                If r.Value > 1 Then
                    If u Is Nothing Then Set u = r Else Set u = Union(u, r)
                End If
            Next
        End With
        For Each r In u.Areas
            Debug.Print r.EntireRow.Address(False, False)
        Next
    End Sub

(白茶) 2022/12/19(月) 13:30:37


 こう表現しても同じですね。
 (まあ、考え方だけ何本も書いたって仕方ないんでしょうけど...^^; )

    Sub test2()
        Dim a As Long, b As Long
        Dim u As Range, r As Range
        With [600:689,660:719,665:669,720:779,750:869,735:764]
            For a = 1 To .Areas.Count
                For b = a + 1 To .Areas.Count
                    Set r = Intersect(.Areas(a), .Areas(b))
                    If Not r Is Nothing Then
                        If u Is Nothing Then Set u = r Else Set u = Union(u, r)
                    End If
                Next
            Next
        End With
        If Not u Is Nothing Then
            For Each r In u.Areas
                Debug.Print r.Address(0, 0)
            Next
        End If
    End Sub

(白茶) 2022/12/19(月) 13:53:24


ありがとうございます。
今、出先なので、戻り次第、結果報告します。
(エビアン) 2022/12/19(月) 14:44:48

数式でやる意味があるのかどうかは分かりませんが

=LET(
_arry,HSTACK(VSTACK(テーブル1[開始],0),VSTACK(0,テーブル1[終了])),
_byr,VSTACK(FALSE,BYROW(_arry,LAMBDA(_a,INDEX(_a,2)<INDEX(_a,1)))),
_scn,CHOOSEROWS(SCAN(0,_byr,LAMBDA(_c,_d,_d+_c)),SEQUENCE(ROWS(_byr)-2,,2)),
_flg1,BYROW(HSTACK(_scn,テーブル1),LAMBDA(_e,SUM((_scn*INDEX(_e,1))*(テーブル1[開始]>=INDEX(_e,2))*(テーブル1[開始]<=INDEX(_e,3)))>1)),
_flg2,BYROW(HSTACK(_scn,テーブル1),LAMBDA(_e,SUM((_scn=INDEX(_e,1))*(テーブル1[終了]>=INDEX(_e,2))*(テーブル1[終了]<=INDEX(_e,3)))>1)),
_unq,UNIQUE(_scn),
_col1,BYROW(_unq,LAMBDA(_f,MAX(IF((_scn=_f)*_flg1,テーブル1[開始])))),
_col2,BYROW(_unq,LAMBDA(_g,MIN(IF((_scn=_g)*_flg2,テーブル1[終了])))),
VSTACK({"重複開始","重複終了"},HSTACK(_col1,_col2))
)
(d-q-t-p) 2022/12/19(月) 15:42:31


少し長くなってしまいましたが数値を増やしても変えても大丈夫です

Option Explicit

Enum 表

    開始列 = 1
    終了列
    重複開始列
    重複終了列
    タイトル行 = 1
End Enum

Sub test2()

    Application.ScreenUpdating = False

    Dim ws As Worksheet: Set ws = ActiveSheet
    Dim 最終行 As Long: 最終行 = ws.Cells(Rows.count, 表.開始列).End(xlUp).Row

    ws.Range(ws.Cells(表.タイトル行 + 1, 表.重複開始列), ws.Cells(最終行, 表.重複終了列)).Clear

    ws.Copy
    Dim Copywb As Workbook: Set Copywb = ActiveWorkbook
    Dim Copyws As Worksheet: Set Copyws = ActiveSheet

    Dim データ一覧: データ一覧 = データ加工して代入(Copyws, 最終行)
    Copywb.Close savechanges:=False

    Dim i As Long
    Dim j As Long: j = 0
    Dim 重複数 As Long: 重複数 = 0
    For i = 1 To UBound(データ一覧, 1)
        If データ一覧(i, 2) = "開始" Then
            重複数 = 重複数 + 1
            If 重複数 = 2 Then
                j = j + 1
                ws.Cells(表.タイトル行, 表.重複開始列).Offset(j) = データ一覧(i, 1)
            End If
        Else
            重複数 = 重複数 - 1
            If 重複数 = 1 Then
                ws.Cells(表.タイトル行, 表.重複終了列).Offset(j) = データ一覧(i, 1)
            End If
        End If
    Next

    Application.ScreenUpdating = True
End Sub

Function データ加工して代入(ws As Worksheet, 最終行 As Long) As Range

    Dim データ数 As Long: データ数 = 最終行 - 表.タイトル行

    ws.Cells(最終行 + 1, 表.開始列).Resize(データ数).Value = _
        ws.Cells(表.タイトル行 + 1, 表.終了列).Resize(データ数).Value
    ws.Cells(表.タイトル行 + 1, 表.終了列).Resize(データ数) = "開始"
    ws.Cells(最終行 + 1, 表.終了列).Resize(データ数) = "終了"

    With ws.Sort
        With .SortFields
            .Clear
            .Add Key:=ws.Cells(表.タイトル行, 表.開始列), Order:=xlAscending
        End With
        .SetRange ws.Cells(表.タイトル行 + 1, 表.開始列).Resize(データ数 * 2, 2)
        .Header = xlNo
        .Apply
    End With

    Set データ加工して代入 = ws.Cells(表.タイトル行 + 1, 表.開始列).Resize(データ数 * 2, 2)
End Function

(ななし) 2022/12/19(月) 17:12:19


 よく読んだら
 >値は0〜1000の整数値
 ってことだったので、
 test2をゼロ値対応してFunction化してみたです。

    Sub Test3()
        Dim v
        [A2:B7].Value = [{600,689;660,719;665,669;720,779;750,869;735,764}]
        v = test4([A2:B7])
        [C2:D2].Resize(UBound(v, 1)) = v
    End Sub
    Function test4(listRng As Variant) As Variant
        Const ICHI = 1&
        Dim v, rA As Range, rB As Range, rI As Range, rU As Range
        Dim a As Long, b As Long
        v = listRng 'Variant/Object/Rangeだった場合対策
        For a = LBound(v, 1) To UBound(v, 1)
            For b = a + 1 To UBound(v, 1)
                Set rA = Range(Cells(v(a, 1) + ICHI, 1), Cells(v(a, 2) + ICHI, 1))
                Set rB = Range(Cells(v(b, 1) + ICHI, 1), Cells(v(b, 2) + ICHI, 1))
                Set rI = Intersect(rA, rB)
                If Not rI Is Nothing Then
                    If rU Is Nothing Then Set rU = rI Else Set rU = Union(rU, rI)
                End If
            Next
        Next
        If rU Is Nothing Then Exit Function
        ReDim v(1 To rU.Areas.Count, 1 To 2)
        For a = 1 To rU.Areas.Count
            v(a, 1) = rU.Areas(a).Row - ICHI
            v(a, 2) = rU.Areas(a).Row + rU.Areas(a).Rows.Count - 1 - ICHI
        Next
        test4 = v
    End Function

(白茶) 2022/12/19(月) 19:39:40


皆様、ご教示ありがとうございます。
返信、遅れて申し訳ありません。

>(白茶)様
test、test2、両方共、期待通りの結果を得られました。
test2は何をやっているか私には理解できませんが、testの方は理解できました。

>(d-q-t-p)様
「すごすぎ」です。こんな壮大な数式になるとは思わなくて。
多分、自分のやり方が間違ってるんでしょうけど、表をテーブル化して試したんですけど
エラーとなりました。
自分で管理できるレベルを超えているので、今回はパスさせてください。
ごめんなさい。せっかく考えて下さったのに...

>(ななし)様
期待通りの結果を得られました。
何をやっているか私には理解できませんが....

自分には(白茶)様のtestが一番理解しやすかったので、
今回はこれを採用させていただきます。
「行数分(例であれば6列)の作業列を用意して、0〜1000の1001セルの連続範囲に「1」を代入して
各行の合計が「2以上」なら重複している」<=こういう事ですよね?

皆様、ありがとうございました。

おっと、ちんたら返信を書いている間に(白茶)様から、さらに返信いただいたみたいで...
自分には敷居が高そうなので、今後の参考とさせてください。
ありがとうございます。

(エビアン) 2022/12/19(月) 20:08:17


 皆さんの力作を勉強させていただいています。

 白茶さんの方法は直感的でいいですね。(直ぐには思いつきませんでした)

 d-q-t-pさんの式による解法は、私の環境では試せないのですが、
 随分と機能拡張されているのだと思わされます。
 自由に変数を定義できたりするので、
 大抵のことは式でできそうな気がしてきます。
 関数型言語としての主要部品は揃った感じでしょうか。

 ななしさんの解法も参考になりました。
 ・開始、終了のイベントを、時刻順にソートしておき、
 ・それぞれのイベントが発生したあとで、継続中の期間の数(「重複数」)を調べ、
 ・それが増加して2になった時点が「重複開始」、
   それが減少して1になった時点が「重複終了」
   と判定する、
 というものですね。

 時刻          重複数 
 600    開始   1      
 660    開始   2      重複開始
 665    開始   3      
 669    終了   2      
 689    終了   1      重複終了
 719    終了   0      
 720    開始   1      
 735    開始   2      重複開始
 750    開始   3      
 764    終了   2      
 779    終了   1      重複終了
 869    終了   0      

 計算量は最小でしょうか。綺麗ですね。
 Enumを使っていて、それに慣れないととっつきにくいかもしれないですが、
 大いに参考になると思いました。
  
(γ) 2022/12/20(火) 10:44:49

コメント返信:

[ 一覧(最新更新順) ]


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