0
votes

I came across those window function pyspark offers and they seem to be quite useful. Unfortunately trying to solve problems I often don't get it to work. Now I wonder if my problem can at all be solved with window function...

Here's my task:

Starting with a dataframe mockup like below:

values = [(0,"a",True,True),(1,"a",True,True),(2,"a",True,True),(3,"a",True,True),(4,"a",True,True),
         (0,"b",False,True),(1,"b",True,True),(2,"b",True,True),(3,"b",False,True),(4,"b",True,True),
         (0,"c",False,True),(1,"c",True,True),(2,"c",True,True),(3,"c",False,True),(4,"c",False,True)]
columns = ['index', 'name', 'Res','solution']
mockup= spark.createDataFrame(values, columns)
mockup.show()
+-----+----+-----+----------------+
|index|name|  Res|default_solution|
+-----+----+-----+----------------+
|    0|   a| true|            true|
|    1|   a| true|            true|
|    2|   a| true|            true|
|    3|   a| true|            true|
|    4|   a| true|            true|
|    0|   b|false|            true|
|    1|   b| true|            true|
|    2|   b| true|            true|
|    3|   b|false|            true|
|    4|   b| true|            true|
|    0|   c|false|            true|
|    1|   c| true|            true|
|    2|   c| true|            true|
|    3|   c|false|            true|
|    4|   c|false|            true|
+-----+----+-----+----------------+

I now want to update the solution column using multiple conditions.

If there are more than 2 false valus per group(name) OR if there are two false values in a group but non of them is at index = 0 the solution column should be false for the whole group, otherwise true.

See the desired outcome:

+-----+----+-----+--------+
|index|name|  Res|solution|
+-----+----+-----+--------+
|    0|   a| true|    true|
|    1|   a| true|    true|
|    2|   a| true|    true|
|    3|   a| true|    true|
|    4|   a| true|    true|
|    0|   b|false|    true|
|    1|   b| true|    true|
|    2|   b| true|    true|
|    3|   b|false|    true|
|    4|   b| true|    true|
|    0|   c|false|   false|
|    1|   c| true|   false|
|    2|   c| true|   false|
|    3|   c|false|   false|
|    4|   c|false|   false|
+-----+----+-----+--------+

I managed to solve the problem with solution following but I hope there is a more elegant way to do this - maybe with windows. For window functions I am always struggling with where to put the window and how to use it in a more complex "when" condition.

My not so great solution :0)

df = mockup.filter(mockup.trip_distance_greater_zero == False).groupby(mockup.name).count()
false_filter_1 = df.filter(F.col('count')>2) \
            .select('name').collect()
false_filter_2 = df.filter(F.col('count')==2) \
            .select('name').collect()
array_false_1 = [str(row['name']) for row in false_filter_1]
array_false_2 = [str(row['name']) for row in false_filter_2]


false_filter_3 = mockup.filter((mockup['index']==0) & (mockup['Res']== False))\
            .select('name').collect()
array_false_3 = [str(row['name']) for row in false_filter_3]

mockup = mockup.withColumn("over_2",
                            F.when((F.col('name').isin(array_false_1)), True).otherwise(False))\
               .withColumn("eq_2",
                            F.when((F.col('name').isin(array_false_2)), True).otherwise(False))\
               .withColumn("at0",
                            F.when((F.col('name').isin(array_false_3)), True).otherwise(False))\
               .withColumn("solution",
                            F.when(((F.col('eq_2')==True) & (F.col('at0')==True)) | (F.col('over_2')==False)&(F.col('eq_2')==False), True).otherwise(False))\
              .drop('over_2')\
              .drop('eq_2')\
              .drop('at0')\
mockup.show()
1

1 Answers

1
votes

Here's my attempt at coding up your description. The output is different from your "expected" output because I guess you dealt with some logic incorrectly? b and c have the same pattern in your dataframe but somehow one of them is true and the other one is false.

from pyspark.sql import functions as F, Window

df2 = mockup.withColumn(
    'false_count',
    F.count(F.when(F.col('Res') == False, 1)).over(Window.partitionBy('name'))
).withColumn(
    'false_at_0',
    F.count(F.when((F.col('Res') == False) & (F.col('index') == 0), 1)).over(Window.partitionBy('name'))
).withColumn(
    'solution',
    ~((F.col('false_count') > 2) | ((F.col('false_count') == 2) & (F.col('false_at_0') != 1)))
)

df2.show()
+-----+----+-----+--------+-----------+----------+
|index|name|  Res|solution|false_count|false_at_0|
+-----+----+-----+--------+-----------+----------+
|    0|   c|false|    true|          2|         1|
|    1|   c| true|    true|          2|         1|
|    2|   c| true|    true|          2|         1|
|    3|   c|false|    true|          2|         1|
|    4|   c| true|    true|          2|         1|
|    0|   b|false|    true|          2|         1|
|    1|   b| true|    true|          2|         1|
|    2|   b| true|    true|          2|         1|
|    3|   b|false|    true|          2|         1|
|    4|   b| true|    true|          2|         1|
|    0|   a| true|    true|          0|         0|
|    1|   a| true|    true|          0|         0|
|    2|   a| true|    true|          0|         0|
|    3|   a| true|    true|          0|         0|
|    4|   a| true|    true|          0|         0|
+-----+----+-----+--------+-----------+----------+

Another perhaps more useful example:

values = [(0,"a",True,True),(1,"a",True,True),(2,"a",True,True),(3,"a",True,True),(4,"a",True,True),
         (0,"b",False,True),(1,"b",True,True),(2,"b",True,True),(3,"b",False,True),(4,"b",True,True),
         (0,"c",True,True),(1,"c",False,True),(2,"c",True,True),(3,"c",False,True),(4,"c",True,True),
         (0,"d",True,True),(1,"d",False,True),(2,"d",False,True),(3,"d",False,True),(4,"d",True,True)]
columns = ['index', 'name', 'Res','solution']
mockup= spark.createDataFrame(values, columns)

which, after being processed by the first code, will give

+-----+----+-----+--------+-----------+----------+
|index|name|  Res|solution|false_count|false_at_0|
+-----+----+-----+--------+-----------+----------+
|    0|   d| true|   false|          3|         0|
|    1|   d|false|   false|          3|         0|
|    2|   d|false|   false|          3|         0|
|    3|   d|false|   false|          3|         0|
|    4|   d| true|   false|          3|         0|
|    0|   c| true|   false|          2|         0|
|    1|   c|false|   false|          2|         0|
|    2|   c| true|   false|          2|         0|
|    3|   c|false|   false|          2|         0|
|    4|   c| true|   false|          2|         0|
|    0|   b|false|    true|          2|         1|
|    1|   b| true|    true|          2|         1|
|    2|   b| true|    true|          2|         1|
|    3|   b|false|    true|          2|         1|
|    4|   b| true|    true|          2|         1|
|    0|   a| true|    true|          0|         0|
|    1|   a| true|    true|          0|         0|
|    2|   a| true|    true|          0|         0|
|    3|   a| true|    true|          0|         0|
|    4|   a| true|    true|          0|         0|
+-----+----+-----+--------+-----------+----------+