如何在Pyspark中获取字符串的模数

时间:2019-07-18 14:20:37

标签: python apache-spark pyspark apache-spark-sql modulus

我想为字母数字的DataFrame列计算相对模数。

在纯Python中,我可以执行类似int(str, base)的操作将其转换为数字值。然后只需应用模数%

例如:

>>> int('5c43466dc6d2870001fk8205', 24) % 64
5L

我当然想避免使用Python中的UDF,而可能仅使用Spark函数。

例如,我的数据源可以是这样的:

df = spark.createDataFrame(
    [
        '5c43466dc6d2870001fk8205', 
        '5c43466dc6d2870001fk8206', 
        '5c43466dc6d2870001fk8207'
    ], 
    StringType()
)

我想要一个新列,其值为[5L, 6L, 7L]

2 个答案:

答案 0 :(得分:2)

@EnzoBnl pointed out一样,有一个函数pyspark.sql.functions.conv将:

  

将字符串列中的数字从一个基数转换为另一个基数。

但是正如他指出的那样,您的人数太大,无法正常使用此功能。

但是,您可以使用一些数学运算将计算简化为易于处理的内容。

可以显示 1 以24为基数的数字mod 64等于该数字模数64的最后两位。也就是说,您可以使用以下代码获得所需的输出:

from pyspark.sql.functions import conv, lit, substring

df.withColumn(
    "mod", 
    conv(substring("value", -2, 2), 24, 10).cast("long") % lit(64).cast("bigint")
).show(truncate=False)
#+------------------------+---+
#|value                   |mod|
#+------------------------+---+
#|5c43466dc6d2870001fk8205|5  |
#|5c43466dc6d2870001fk8206|6  |
#|5c43466dc6d2870001fk8207|7  |
#+------------------------+---+

需要强制转换为long,我有消息来源解释原因,但目前似乎找不到。


权利要求证明1:如果d是数字的以24为底的表示形式,则为d % 64 = d_low % 64,其中d_low代表d的两个最低有效数字。

让我们拨打我们的24号码d。如果dn位数字,则可以用十进制(以10为底)表示,如下所示:

d = sum( di * 24**i for i in range(n) )

其中di代表以10为底的ith中的d位。

我们可以等效地将该总和写为低2位(2个最低有效数字)和高n-2位(给定n > 2)的总和:

d = sum( di * 24**i for i in range(2) ) + sum( di * 24**i for i in range(2, n) )
#   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#           let's call this d_low                  let's call this d_high

d = d_low + d_high

请注意,通过排除d_high可以简化24**2

d_high = (24**2) * sum( di * 24**(i-2) for i in range(2, n) )
#                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#                   for simplicity, let's call this x

d_high = (24**2) * x

因此我们有:

d = d_low + (24**2) * x

现在您要计算的数字为d % 64

d % 64 = (d_low + (24**2) * x) % 64

here(x + y) % z = ( x % z + y % z ) % z所示,因此以上内容可以写为:

d % 64 = (d_low % 64 + ((24**2) * x) % 64) % 64

现在观察到24**264的偶数倍(因为它们都包含2**6

24**2=((2**3)*3)**2=((2**6)*(3**2))=64*9`. 

因此(24**2) % 64 = 0。然后是((24**2) * x) % 64 = 0

因此,我们现在可以编写:

d % 64 = (d_low % 64 + 0 % 64) % 64
       = (d_low % 64 + 0) % 64
       = d_low % 64

答案 1 :(得分:1)

  • 有一个内置函数可以在两个碱基之间进行转换:conv(num, from_base, to_base)conv('100', 2, 10)给出4。我将其指向documentation

  • 假设您操作的是基数24,则可以使用n实现“模substr(conv(value, 24, n), -1)”的运算。

  • 但是我认为事情将无法进行,因为在您的情况下,值要优于2^64-1,即以24为底的l12ee5fn0ji1if