Python слияние двух массивов Numpy на основе условия - PullRequest
0 голосов
/ 12 декабря 2018

Как объединить следующие два массива, просмотрев значение из массива A в массиве B?

Массив 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)

и Массив B:

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

Целью здесь является создание массива C путем поиска IPv4-адреса из массива B в массиве A, сравнения их, получения второго значения соответствующего массива и его сохранения:

Array C:

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

IP-адреса этого типа: https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_network

Как мне этого добиться?

edit:

Обратите внимание что слияние обусловлено сопоставлением IP-адресов, поэтому результирующий массив C будет иметь такое же количество массивов, что и массив B, но у него будет еще одно значение.Предлагаемые повторяющиеся ссылки не отвечают на один и тот же вопрос.

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Похоже, вы не не можете использовать панд.Если ваши IP-адреса идеально совпадают, вы можете merge, а затем использовать pd.DataFrame.values для возврата массива NumPy:

import pandas as pd

# data from @mk18
df_A = pd.DataFrame(array_A[:, 1:], columns=['', 'IP'])
df_B = pd.DataFrame(array_B, columns=['id', 'value', 'IP', 'na'])

res = df_B.merge(df_A, on='IP').values

print(res)

array([['123456', 'A1', IPv4Network('192.168.0.0/28'), nan, 'AF'],
       ['987654', 'B1', IPv4Network('192.168.0.0/29'), nan, 'AE']],
      dtype=object)

Если вы хотите игнорироватьсетевой компонент и включают только network_address при объединении, т.е. используйте '1.2.3.5' вместо '1.2.3.5/24', тогда вы можете создать вспомогательный ряд перед объединением:

import pandas as pd
from operator import attrgetter

df_A = pd.DataFrame(array_A[:, 1:], columns=['key', 'IP'])
df_B = pd.DataFrame(array_B, columns=['id', 'value', 'IP', 'na'])

df_A['IP_NoNetwork'] = df_A['IP'].map(attrgetter('network_address'))
df_B['IP_NoNetwork'] = df_B['IP'].map(attrgetter('network_address'))

res = df_B.merge(df_A.drop('IP', 1), on='IP_NoNetwork')\
          .loc[:, ['id', 'value', 'IP', 'na', 'key']].values
0 голосов
/ 20 декабря 2018

Существуют проблемы с вашими данными и сложности, которые мешают вам использовать join_by или rec_join в качестве предложенного вами вопроса.

Основная проблема с вашими данными, как указали другие, заключается в том, чточто сети, подобные IPv4Network('1.2.3.4/24'), не являются действительными сетями, поскольку в них установлены биты хоста, которые маскируются /24./24 означает, что последние 32 - 24 = 8 биты являются битами вашего хоста, а конструктор для IPv4Network требует, чтобы они были установлены в 0, например, IPv4Network('1.2.3.0/24') является действительным.

Основное осложнение заключается в том, чтоу вас есть сетей в одном массиве, но адресов в другом.Такие методы, как rec_join и join_by, используют сравнение (т. Е. ==), чтобы решить, какие записи идут вместе.Некоторые из предложенных ответов «решают» это, заменяя ваши сети адресами, но это не является вашей проблемой.

Также обратите внимание, что один сетевой адрес может попадать в несколько разных сетей.Например, IPv4Address('1.2.3.129') находится в пределах IPv4Network('1.2.3.0/24') и IPv4Network('1.2.3.128/25').Итак, я предполагаю, что вы ожидаете, что оба совпадения будут отображаться в ваших результатах.

Чтобы объединить адреса из одного массива в сети, в которые фактически входит этот адрес, вам придется самостоятельно выполнить итерацию по массиву и создатьновенький.Тип работающего сравнения: IPv4Address('1.2.3.129') in IPv4Network('1.2.3.0/24') (это True).

Пример рабочего кода, объединяющий это:

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 голосов
/ 17 декабря 2018

Это должно делать то, о чем вы просили (по крайней мере, вывод - именно то, что вы хотели), я сделал несколько небольших предположений, чтобы справиться с вашей #dummydata, но это не должно иметь большого значения.

Код:

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))

Вывод:

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

Примечание:

Это решение имеет O(m * n) сложность, которая не обязательна, есть много готовых (Pandas) и нестандартных (например, с использованием dict) способов слияния с меньшей сложностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...