将带有int标志的列转换为pyspark中的字符串数组

将带有int标志的列转换为pyspark中的字符串数组,pyspark,apache-spark-sql,pyspark-dataframes,Pyspark,Apache Spark Sql,Pyspark Dataframes,我有一个dataframe,它有一个名为“traits”的列,它是一个由多个标志组成的整数 我需要将此列转换为字符串列表(用于弹性搜索索引)。转换看起来像这样 TRAIT_0 = 0 TRAIT_1 = 1 TRAIT_2 = 2 def flag_to_list(flag: int) -> List[str]: trait_list = [] if flag & (1 << TRAIT_0): trait_list.append("TR

我有一个dataframe,它有一个名为“traits”的列,它是一个由多个标志组成的整数

我需要将此列转换为字符串列表(用于弹性搜索索引)。转换看起来像这样

TRAIT_0 = 0
TRAIT_1 = 1
TRAIT_2 = 2
def flag_to_list(flag: int) -> List[str]:
    trait_list = []
    if flag & (1 << TRAIT_0):
        trait_list.append("TRAIT_0")
    elif flag & (1 << TRAIT_1):
        trait_list.append("TRAIT_1")
    elif flag & (1 << TRAIT_2):
        trait_list.append("TRAIT_2")

    return trait_list
TRAIT_0=0
特质_1=1
性状2=2
def flag_to_list(flag:int)->list[str]:
特征列表=[]

if flag&(1我们可以定义一个UDF来包装函数,然后调用它。下面是一些示例代码:

输入导入列表中的

从pyspark.sql.types导入ArrayType、StringType
特征0=0
特质_1=1
性状2=2
def flag_to_list(flag:int)->list[str]:
特征列表=[]

if flag&(1我们可以定义一个UDF来包装函数,然后调用它。下面是一些示例代码:

输入导入列表中的

从pyspark.sql.types导入ArrayType、StringType
特征0=0
特质_1=1
性状2=2
def flag_to_list(flag:int)->list[str]:
特征列表=[]

如果使用flag&(1IIUC),您可以尝试SparkSQL内置函数:(1)使用+将整数(base-10)->二进制(base-2)->字符串->字符串数组(反转),(2)基于
0
1
值及其数组索引,并将数组转换为相应的命名特征数组:

from pyspark.sql.functions import expr

df = spark.createDataFrame([("name1", 5),("name2", 1),("name3", 0),("name4", 12)], ['name', 'traits'])
#DataFrame[name: string, traits: bigint]

traits = [ "Traits_{}".format(i) for i in range(8) ]
traits_array = "array({})".format(",".join("'{}'".format(e) for e in traits))
# array('Traits_0','Traits_1','Traits_2','Traits_3','Traits_4','Traits_5','Traits_6','Traits_7')

sql_expr = """

    filter(
      transform(
        /* convert int -> binary -> string -> array of strings, and then reverse the array */
        reverse(split(string(conv(traits,10,2)),'(?!$)')),
        /* take the corresponding items from the traits_array when value > 0, else NULL */
        (x,i) -> {}[IF(x='1',i,NULL)]
      ),
      /* filter out NULL items from the array */
      y -> y is not NULL
    ) AS trait_list

""".format(traits_array)
# filter(
#   transform(
#     reverse(split(string(conv(traits,10,2)),'(?!$)')),
#     (x,i) -> array('Traits_0','Traits_1','Traits_2','Traits_3','Traits_4','Traits_5','Traits_6','Traits_7')[IF(x='1',i,NULL)]
#   ),
#   y -> y is not NULL
# )

df.withColumn("traits_list", expr(sql_expr)).show(truncate=False)
+-----+------+--------------------+
|name |traits|traits_list         |
+-----+------+--------------------+
|name1|5     |[Traits_0, Traits_2]|
|name2|1     |[Traits_0]          |
|name3|0     |[]                  |
|name4|12    |[Traits_2, Traits_3]|
+-----+------+--------------------+
下面是运行
反向(split(string(conv(traits,10,2)),“(?!$)”)
后的结果,请注意,split模式
(?!$)
用于避免显示为最后一个数组项的空值

df.selectExpr("*", "reverse(split(string(conv(traits,10,2)),'(?!$)')) as t1").show()
+-----+------+------------+
| name|traits|          t1|
+-----+------+------------+
|name1|     5|   [1, 0, 1]|
|name2|     1|         [1]|
|name3|     0|         [0]|
|name4|    12|[0, 0, 1, 1]|
+-----+------+------------+

IIUC,您可以尝试SparkSQL内置函数:(1)使用+将整数(base-10)->二进制(base-2)->字符串->字符串数组(反转),(2)基于
0
1
值及其数组索引,并将数组转换为相应的命名特征数组:

from pyspark.sql.functions import expr

df = spark.createDataFrame([("name1", 5),("name2", 1),("name3", 0),("name4", 12)], ['name', 'traits'])
#DataFrame[name: string, traits: bigint]

traits = [ "Traits_{}".format(i) for i in range(8) ]
traits_array = "array({})".format(",".join("'{}'".format(e) for e in traits))
# array('Traits_0','Traits_1','Traits_2','Traits_3','Traits_4','Traits_5','Traits_6','Traits_7')

sql_expr = """

    filter(
      transform(
        /* convert int -> binary -> string -> array of strings, and then reverse the array */
        reverse(split(string(conv(traits,10,2)),'(?!$)')),
        /* take the corresponding items from the traits_array when value > 0, else NULL */
        (x,i) -> {}[IF(x='1',i,NULL)]
      ),
      /* filter out NULL items from the array */
      y -> y is not NULL
    ) AS trait_list

""".format(traits_array)
# filter(
#   transform(
#     reverse(split(string(conv(traits,10,2)),'(?!$)')),
#     (x,i) -> array('Traits_0','Traits_1','Traits_2','Traits_3','Traits_4','Traits_5','Traits_6','Traits_7')[IF(x='1',i,NULL)]
#   ),
#   y -> y is not NULL
# )

df.withColumn("traits_list", expr(sql_expr)).show(truncate=False)
+-----+------+--------------------+
|name |traits|traits_list         |
+-----+------+--------------------+
|name1|5     |[Traits_0, Traits_2]|
|name2|1     |[Traits_0]          |
|name3|0     |[]                  |
|name4|12    |[Traits_2, Traits_3]|
+-----+------+--------------------+
下面是运行
反向(split(string(conv(traits,10,2)),“(?!$)”)
后的结果,请注意,split模式
(?!$)
用于避免显示为最后一个数组项的空值

df.selectExpr("*", "reverse(split(string(conv(traits,10,2)),'(?!$)')) as t1").show()
+-----+------+------------+
| name|traits|          t1|
+-----+------+------------+
|name1|     5|   [1, 0, 1]|
|name2|     1|         [1]|
|name3|     0|         [0]|
|name4|    12|[0, 0, 1, 1]|
+-----+------+------------+

如果您能提供样本可复制数据和所需输出,这将有所帮助。您的spark版本是什么?编辑我的问题,pyspark版本是2.4.5,并提供输入和输出数据。如果您能提供样本可复制数据和所需输出,这将有所帮助。您的spark版本是什么?编辑我的问题,pyspark版本是2。4.5并提供了输入和输出数据。将此标记为正确答案,因为使用SparkSql函数比基于UDF的方法更有效,尽管此方法要复杂得多。将此标记为正确答案,因为使用SparkSql函数比基于UDF的方法更有效,即使此方法是更复杂。