如何通过从数组B中的数组A中查找一个值来合并以下两个数组?
数组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,方法是从数组A中的数组B中查找IPv4Address并进行比较,然后获取相应数组的第二个值并将其存储:
数组C:
array([['123456', 'A1', IPv4Address('1.2.3.5'), nan, 'AF'],
['987654', 'B1', IPv4Address('1.2.3.47'), nan, 'AE']],
dtype=object)
ip地址的类型为:ServiceHostFactory
我该如何实现?
请注意,合并的条件是IP匹配,因此生成的数组C的数组数与数组B的数组数相同,但其值更大。建议的重复链接不能回答相同的问题。
答案 0 :(得分:6)
这应该可以满足您的要求(至少输出正是您想要的),我做了一些小的假设来处理您的#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
)合并的方式较低的复杂性。
答案 1 :(得分:2)
您似乎没有理由不能使用熊猫。如果您的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
答案 2 :(得分:0)
您的数据存在一些问题,并且复杂性使您无法按照所链接的问题使用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)