94541


### 目標

データ1とデータ2をLeft Joinで結合させ、セグメント情報を取り込みたい。

データ１

[code]

    csv_data =　'''
    送信元IP,宛先IP,宛先ポート番号
    1.1.1.1,3.3.3.3,53
    1.1.1.2,3.3.3.3,57
    2.2.2.2,9.9.9.9,54
    2.2.2.3,9.9.9.9,59
    4.4.4.4,10.10.10.10,56
    '''
    df1 = pd.read_csv(io.StringIO(csv_data))
    
[/code]

データ2

[code]

    csv_data'''
    IPアドレス,サブネットマスク,セグメント
    1.1.1.0,255.255.255.0,東京    
    2.2.2.0,255.255.255.128,大阪
    4.4.4.0,255.255.255.128,福岡  
    5.5.5.0,255.255.255.240,東京  
    '''
    df2 = pd.read_csv(io.StringIO(csv_data))
    
[/code]

### これまでに試したこと

1.データ結合のために、以下のコードでデータ1と同名のカラムを作成。

[code]

    df2["IPアドレス／サブネットマスク"]=df2["Ipアドレス"]+"/"+df["サブネットマスク"]
    
[/code]

2.ipaddressモジュールでホストアドレスを取得

[code]

    import ipaddress
    
    hostaddress= ipaddress.IPv4Network(df["IPアドレス／サブネットマスク"])
    
    host_list = []
    for addr in netaddr24.hosts():
        addr
        host_list.append(addr)
    
[/code]

でホストアドレスを取得できると思いましたが、AddressValueError: Only one '/' permitted in 0  
のエラーが出て実現できませんでした。  
また、以下のコードがネットにありましたが、`summarized_networks` からホストアドレスをどのように取得して良いのかが分かりませんでした。

[code]

    from ipaddress import (
        ip_network,
        collapse_addresses,
    )
    prefixes = map(ip_network,df2["IPアドレス／サブネットマスク"])
    summarized_networks = collapse_addresses(prefixes)
    
[/code]

ホストアドレスをどのように取得すれば良いのかが良く分かっていませんが、仮に取得したホストアドレスで新たな変数をdf2で送信元IPの名前で作成すれば、

[code]

    merger=pd.merge(df1,df2,on="送信元IP",how="left")
    
[/code]

で実現可能と思っています。ご教示いただけますと幸甚です。


CC BY-SA 4.0
2023-04-16T14:16:21.863
94540
2023-04-18T17:17:38.877
2023-04-17T01:09:36.383
3060
57568
question
python
IPアドレスを元にしたデータの結合について

[code]

    import io
    import pandas as pd
    from ipaddress import ip_network, ip_address
    
    csv_data = '''
    送信元IP,宛先IP,宛先ポート番号
    1.1.1.1,3.3.3.3,53
    1.1.1.2,3.3.3.3,57
    2.2.2.2,9.9.9.9,54
    2.2.2.3,9.9.9.9,59
    4.4.4.4,10.10.10.10,56
    20.20.20.20,30.30.30.30,60
    '''
    df1 = pd.read_csv(io.StringIO(csv_data))
    
    csv_data = '''
    IPアドレス,サブネットマスク,セグメント
    1.1.1.0,255.255.255.0,東京
    2.2.2.0,255.255.255.128,大阪
    4.4.4.0,255.255.255.128,福岡
    5.5.5.0,255.255.255.240,東京
    '''
    df2 = pd.read_csv(io.StringIO(csv_data))
    
    #
    networks = df2[['IPアドレス', 'サブネットマスク']].apply(lambda x: ip_network('/'.join(x)), axis=1)
    network_addresses = networks.apply(lambda x: int(x.network_address))
    netmasks = networks.apply(lambda x: int(x.netmask))
    def match_segment(ip_addr):
        idx = (int(ip_address(ip_addr)) & netmasks) == network_addresses
        return df2.loc[idx, 'セグメント'].values[0] if idx.any() else '不明'
    
    df1['セグメント'] = df1['送信元IP'].apply(match_segment)
    print(df1)
    
[/code]

送信元IP | 宛先IP | 宛先ポート番号 | セグメント  
---|---|---|---  
1.1.1.1 | 3.3.3.3 | 53 | 東京  
1.1.1.2 | 3.3.3.3 | 57 | 東京  
2.2.2.2 | 9.9.9.9 | 54 | 大阪  
2.2.2.3 | 9.9.9.9 | 59 | 大阪  
4.4.4.4 | 10.10.10.10 | 56 | 福岡  
20.20.20.20 | 30.30.30.30 | 60 | 不明  
  
### 追記

IPv4 アドレスとサブネットマスクを 32 ビット整数(`uint32`)値に変換して `bit
and`(ビット論理積)を取るとネットワークアドレスに相当する整数値になります。例えば、`2.2.2.3` の場合は以下の様になります。

[code]

    >>> int(ip_address('2.2.2.3')) & netmasks
    0    33686016
    1    33686016
    2    33686016
    3    33686016
    dtype: int64
    
    >>> network_addresses
    0    16843008
    1    33686016
    2    67372032
    3    84215040
    dtype: int64
    
    >>> idx = (int(ip_address('2.2.2.3')) & netmasks) == network_addresses
    >>> idx
    0    False
    1     True
    2    False
    3    False
    dtype: bool
    
[/code]

`idx` は boolean インデックスなので、`df2[idx]` とすると `idx` が `True`
の行が抽出されます。セグメント列の値を取得する場合は `loc` メソッドを使います。

[code]

    >>> df2[idx]
      IPアドレス サブネットマスク セグメント
    1    2.2.2.0  255.255.255.128       大阪
    
    >>> df2.loc[idx, 'セグメント']
    1    大阪
    Name: セグメント, dtype: object
    
    >>> df2.loc[idx, 'セグメント'].values[0]
    '大阪'
    
[/code]


CC BY-SA 4.0
2023-04-16T18:53:23.443
94541
2023-04-18T17:17:38.877
2023-04-18T17:17:38.877
47127
47127
94540
answer