这就是我在pyspark
中创建具有原始数据类型的数据框的方法:
from pyspark.sql.types import StructType, StructField, DoubleType, StringType, IntegerType
fields = [StructField('column1', IntegerType(), True), StructField('column2', IntegerType(), True)]
schema = StructType(fields)
df = spark.createDataFrame([], schema)
values = [tuple([i]) +
tuple([i])
for i in range(3)]
df = spark.createDataFrame(values, schema)
现在,如果我想在字典数据中添加第三列,例如:{“ 1”:1.0,“ 2”:2.0,“ 3”:3.0},该怎么办? 我要创建此数据框:
+--------------------+-----------------+------------------------------+
|column1 |column2 |column3 |
+--------------------+-----------------+------------------------------+
|1 |1 |{"1": 1.0, "2": 1.0, "3": 1.0}|
+--------------------+-----------------+------------------------------+
|2 |2 |{"1": 2.0, "2": 2.0, "3": 2.0}|
+--------------------+-----------------+------------------------------+
|3 |3 |{"1": 3.0, "2": 3.0, "3": 3.0}|
+--------------------+-----------------+------------------------------+
有一个MapType似乎有用,但是我不知道如何使用它?
并假设已创建数据框,如何根据给定的dict选择具有该dict值的数据行来基于第三列对其进行过滤?
答案 0 :(得分:2)
示例创建方式:
from pyspark.sql.types import MapType, IntegerType, DoubleType, StringType, StructType, StructField
import pyspark.sql.functions as f
schema = StructType([
StructField('column1', IntegerType()),
StructField('column2', IntegerType()),
StructField('column3', MapType(StringType(), DoubleType()))])
data = [(1, 2, {'a':3.5, 'b':4.2}), (4, 8, {'b':3.7, 'e':4.9})]
df = spark.createDataFrame(data, schema=schema)
df.show()
输出:
+-------+-------+--------------------+
|column1|column2| column3|
+-------+-------+--------------------+
| 1| 2|[a -> 3.5, b -> 4.2]|
| 4| 8|[e -> 4.9, b -> 3.7]|
+-------+-------+--------------------+
关于如何过滤DataFrame仅留下具有特定键的元素的示例(假设您在地图中没有null值,并且您的Spark版本为2.4+,则早期版本没有element_at
):
filtered_df = df.where(f.element_at(df.column3, 'a').isNotNull())
输出:
+-------+-------+--------------------+
|column1|column2| column3|
+-------+-------+--------------------+
| 1| 2|[a -> 3.5, b -> 4.2]|
+-------+-------+--------------------+
我可能会误解了您的问题-如果您的意图是只保留map列等于特定词典的行,那将比较棘手。据我所知,Spark在字典类型上没有比较操作(这是不寻常的操作)。有一种方法可以使用udf来实现,但效率不高。该代码可能如下所示:
from pyspark.sql.types import MapType, IntegerType, DoubleType, StringType, StructType, StructField, BooleanType
my_dict = {'b':2.7, 'e':4.9}
from pyspark.sql.functions import udf
def map_equality_comparer(my_dict):
@udf(BooleanType())
def comparer(m):
if len(m) != len(my_dict): return False
for k, v in m.items():
if my_dict.get(k) != v: return False
return True
return comparer
filtered_df = df.where(map_equality_comparer(my_dict)(df.column3))
filtered_df.show()
如果这对于您来说太慢了,您可以考虑创建字典的规范表示形式并进行比较(例如,将字典转换为键值对的排序数组,并根据这些数组的相等性进行过滤)。
答案 1 :(得分:2)
您可以在不使用UDF的情况下使用 create_map(spark2.0+)
来执行此操作,还可以使用要使用的字典创建 MapType
的新列,然后使用如下所示的过滤器来获取 my_dict
等于 column3
的所有行。
from pyspark.sql import functions as F
df.show() #sample dataframe
my_dict = {'b':3.7, 'e':4.9} #dictionary to filter with
#+-------+-------+--------------------+
#|column1|column2| column3|
#+-------+-------+--------------------+
#| 1| 2|[a -> 3.5, b -> 4.2]|
#| 4| 8|[e -> 4.9, b -> 3.7]|
#+-------+-------+--------------------+
from pyspark.sql import functions as F
df.withColumn("map", F.create_map(*[item for sublist in [[F.lit(x),F.lit(y)]\
for x,y in my_dict.items()] for item in sublist]))\
.filter(' and '.join(["column3.{0}=map.{0}".format(x) for x in my_dict.keys()])+\
' and size(column3)=size(map)').drop("map").show()
#+-------+-------+--------------------+
#|column1|column2| column3|
#+-------+-------+--------------------+
#| 4| 8|[e -> 4.9, b -> 3.7]|
#+-------+-------+--------------------+