【问题标题】:Check if an IP address is in a IPNetwork with Pyspark使用 Pyspark 检查 IP 地址是否在 IPNetwork 中
【发布时间】:2018-06-28 13:49:10
【问题描述】:

使用 Pyspark,如果数据帧 A 中的 IP 地址在 IP 网络范围内或命中数据帧 B 中的相同 IP 地址,我想加入/合并。

数据帧 A 仅包含 IP 地址,而另一个数据帧具有 IP 地址或带有 CIDR 的 IP 地址。这是一个例子。

Dataframe A
+---------------+
|     ip_address|
+---------------+
|      192.0.2.2|
|   164.42.155.5|
|    52.95.245.0|
|  66.42.224.235|
|            ...|
+---------------+

Dataframe B
+---------------+
|     ip_address|
+---------------+
| 123.122.213.34|
|    41.32.241.2|
|  66.42.224.235|
|   192.0.2.0/23|
|            ...|
+---------------+

那么预期的输出如下所示

+---------------+--------+
|     ip_address| is_in_b|
+---------------+--------+
|      192.0.2.2|    true|  -> This is in the same network range as 192.0.2.0/23
|   164.42.155.5|   false|
|    52.95.245.0|   false|
|  66.42.224.235|    true|  -> This is in B
|            ...|     ...|
+---------------+--------+

我首先想尝试的想法是使用 udf 逐一比较并在 CIDR 出现时检查 IP 范围,但似乎 udf 没有多个数据帧。我还尝试将 df B 转换为列表,然后进行比较。但是,由于A行数*B行数超过1亿,效率非常低,耗时较长。有没有有效的解决方案?

编辑: 有关更多详细信息,我使用以下代码在不使用 pyspark 并使用任何库的情况下进行检查。

def cidr_to_netmask(c):
    cidr = int(c)
    mask = (0xffffffff >> (32 - cidr)) << (32 - cidr)

    return (str((0xff000000 & mask) >> 24) + '.' + str((0x00ff0000 & mask) >> 16) + '.' + str((0x0000ff00 & mask) >> 8) + '.' + str((0x000000ff & mask)))

def ip_to_numeric(ip):
    ip_num = 0
    for i, octet in enumerate(ip.split('.')):
        ip_num += int(octet) << (24 - (8 * i))

    return ip_num

def is_in_ip_network(ip, network_addr):
    if len(network_addr.split('/')) < 2:
        return ip == network_addr.split('/')[0]
    else:
        network_ip, cidr = network_addr.split('/')
        subnet = cidr_to_netmask(cidr)
        return (ip_to_numeric(ip) & ip_to_numeric(subnet)) == (ip_to_numeric(network_ip) & ip_to_numeric(subnet))

【问题讨论】:

  • 我不熟悉“在 IP 网络范围内”的逻辑——你能举例说明一下吗?还有为什么52.95.245.066.42.224.235 在输出中显示为false?这些显然在 B 中。我错过了什么吗?
  • @pault 我只是想展示一个输出示例,所以我修改了示例数据帧。对于网络范围,gist.github.com/tott/7684443 即使在 PHP 中也是一个很好的例子。在 Python 中,我通常使用 netaddr 库来执行与 IPAddress(x) in IPNetwork(y) 相同的操作。 netaddr.readthedocs.io/en/latest/tutorial_01.html

标签: python pyspark apache-spark-sql


【解决方案1】:

您可以使用crossJoinudfs,但要付出笛卡尔积的成本

from pyspark.sql import *
data_1 = ["192.0.2.2", "164.42.155.5", "52.95.245.0", "66.42.224.235"]
data_2 = ["192.0.2.0/23", "66.42.224.235"]
DF1 = spark.createDataFrame([Row(ip=x) for x in data_1])
DF2 = spark.createDataFrame([Row(ip=x) for x in data_2])

from pyspark.sql.functions import udf
from pyspark.sql.types import *
join_cond = udf(is_in_ip_network, BooleanType())

DF1.crossJoin(DF2).withColumn("match",join_cond(DF1.ip, DF2.ip))

结果看起来类似于

ip          ip              match 
192.0.2.2   192.0.2.0/23    true
192.0.2.2   66.42.224.235   false
164.42.155.5    192.0.2.0/23    false
164.42.155.5    66.42.224.235   false
52.95.245.0 192.0.2.0/23    false
52.95.245.0 66.42.224.235   false
66.42.224.235   192.0.2.0/23    false
66.42.224.235   66.42.224.235   true

【讨论】:

  • 感谢它在加入后删除重复项的效果很好。但是,由于 DF1 有大约 10,000,000 行,而 DF2 大约有 10,000 行,它仍然需要太长时间。有什么办法可以优化吗? DF1 有一些重复的 IP 地址(DF2 中的所有 IP 都是唯一的,我需要将所有重复的 IP 地址保留在 DF1 中。)所以我想知道我是否可以使用类似 pyspark 的记忆技术。
  • @andrewshih 您可以使用广播哈希联接,它将在所有执行程序上缓存 df2。例如:large_df.join(broadcast(small_df), ["key"])
猜你喜欢
  • 2013-08-22
  • 2015-11-03
  • 2012-01-25
  • 1970-01-01
  • 2017-09-02
  • 2017-05-21
  • 2012-11-28
  • 2017-05-05
  • 1970-01-01
相关资源
最近更新 更多