web-dev-qa-db-fra.com

Python fusionner deux tableaux Numpy en fonction de la condition

Comment puis-je fusionner les deux tableaux suivants en recherchant une valeur du tableau A dans le tableau B?

Tableau A: 

array([['GG', 'AB', IPv4Network('1.2.3.41/26')],
       ['GG', 'AC', IPv4Network('1.2.3.42/25')],
       ['GG', 'AD', IPv4Network('1.2.3.43/24')],
       ['GG', 'AE', IPv4Network('1.2.3.47/23')],
       ['GG', 'AF', IPv4Network('1.2.3.5/24')]],
      dtype=object)

et tableau B:

array([['123456', 'A1', IPv4Address('1.2.3.5'), nan],
       ['987654', 'B1', IPv4Address('1.2.3.47'), nan]],
      dtype=object)  

Le but ici est de créer le tableau C, en recherchant l'adresse IPv4Address du tableau B dans le tableau A, en obtenant la deuxième valeur du tableau correspondant et en la stockant:

Tableau C: 

array([['123456', 'A1', IPv4Address('1.2.3.5'), nan, 'AF'],
       ['987654', 'B1', IPv4Address('1.2.3.47'), nan, 'AE']],
      dtype=object) 

Les adresses IP sont de ce type: https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_network

Comment puis-je atteindre cet objectif?

modifier:

Veuillez noter que la fusion est conditionnée par la correspondance des IP, de sorte que le tableau C résultant aura le même nombre de tableaux que le tableau B, mais qu'il aura une valeur supplémentaire. Les liens en double suggérés ne répondent pas à la même question.

8
storis

Cela devrait faire ce que vous aviez demandé (au moins la sortie correspond exactement à ce que vous vouliez), j’ai formulé quelques hypothèses mineures pour traiter votre #dummydata, mais cela ne devrait pas avoir trop d’importance.

Code:

import numpy as np
import ipaddress as ip

array_A = np.array([['GG', 'AB', ip.ip_network('192.168.0.0/32')],
                    ['GG', 'AC', ip.ip_network('192.168.0.0/31')],
                    ['GG', 'AD', ip.ip_network('192.168.0.0/30')],
                    ['GG', 'AE', ip.ip_network('192.168.0.0/29')],
                    ['GG', 'AF', ip.ip_network('192.168.0.0/28')]],
                   dtype=object)

array_B = np.array([['123456', 'A1', ip.ip_network('192.168.0.0/28'), np.nan],
                    ['987654', 'B1', ip.ip_network('192.168.0.0/29'), np.nan]],
                   dtype=object)


def merge_by_ip(A, B):
    # initializing an empty array with len(B) rows and 5 columns for the values you want to save in it
    C = np.empty([len(B), 5],dtype=object)
    for n in range(len(B)):
        for a in A:
            # checking condition: if ip address in a is ip address in b
            if a[2] == B[n][2]:
                # add the entry of b with the second value of a to the new Array c
                C[n] = np.append(B[n], a[1])
    return C


print(merge_by_ip(array_A, array_B))

Sortie:

[['123456' 'A1' IPv4Network('192.168.0.0/28') nan 'AF']
 ['987654' 'B1' IPv4Network('192.168.0.0/29') nan 'AE']]

Remarque:

Cette solution a la complexité O(m * n), ce qui n'est pas nécessaire. Il existe de nombreuses méthodes prêtes à l'emploi (Pandas) et personnalisées (par exemple, en utilisant dict) pour fusionner avec une complexité inférieure.

6
mrk

Il existe des problèmes avec vos données et des complications qui vous empêchent d'utiliser join_by ou rec_join comme suggéré dans la question que vous avez liée.

Le problème principal avec vos données, comme l'ont souligné d'autres personnes, est que des réseaux tels que IPv4Network('1.2.3.4/24') ne sont pas des réseaux valides, car ils ont des bits d'hôte définis masqués par le /24. Le /24 signifie que les derniers bits 32 - 24 = 8 sont vos bits d'hôtes et que le constructeur de IPv4Network exige que ces derniers soient définis sur 0; par exemple, IPv4Network('1.2.3.0/24') est valide.

La principale complication est que vous avez réseaux dans un tableau, mais adresses dans l'autre. Des méthodes telles que rec_join et join_by utilisent la comparaison (c'est-à-dire ==) pour décider quels enregistrements vont ensemble. Certaines des réponses proposées «résolvent» cela en remplaçant vos réseaux par des adresses, mais cela ne semble pas être votre problème.

Notez également qu'une seule adresse réseau peut appartenir à plusieurs réseaux différents. Par exemple, IPv4Address('1.2.3.129') relève à la fois de IPv4Network('1.2.3.0/24') et de IPv4Network('1.2.3.128/25'). Donc, je suppose que vous vous attendez à ce que les deux matchs apparaissent dans vos résultats.

Pour joindre des adresses d'un groupe à des réseaux dans lesquels se trouve l'adresse, vous devez parcourir vous-même le tableau et en construire un nouveau. Le type de comparaison qui fonctionne est IPv4Address('1.2.3.129') in IPv4Network('1.2.3.0/24') (il s'agit de True).

Un exemple de code fonctionnel réunissant ceci:

from numpy import nan, asarray, concatenate
from ipaddress import IPv4Address, IPv4Network

a = asarray([
    ['GG', 'AA', IPv4Network('1.2.4.0/24')],
    ['GG', 'AB', IPv4Network('1.2.3.128/25')],
    ['GG', 'AC', IPv4Network('1.2.3.0/24')]
], dtype=object)

b = asarray([
    ['123456', 'A1', IPv4Address('1.2.3.4'), nan],
    ['987654', 'B1', IPv4Address('1.2.3.129'), nan],
    ['024680', 'C1', IPv4Address('1.2.4.0'), nan]
], dtype=object)


def join_addresses_networks(addresses, networks):
    for address in addresses:
        for network in networks:
            if address[2] in network[2]:
                yield concatenate((address, network[:-1]))


c = asarray(list(join_addresses_networks(b, a)))

print(c)
0
Grismar