Фильтрация фреймов данных по нескольким столбцам с различными условиями в зависимости от значений столбцов - PullRequest
2 голосов
/ 05 апреля 2019

Я пытаюсь отфильтровать фрейм данных по нескольким столбцам. Проблема в том, что условие изменяется со значением столбца.

Например, у меня есть следующий набор необработанных данных.

df_assets = pd.DataFrame(
    {'asset_id': ['10092', '39883', '82883', '28883', '09093', '27817', '38261', '28372', '38472', '39738'],
     'asset_type' :['laptop', 'mousepad', 'mouse', 'pen', 'pen', 'monitor', 'wire', 'laptop', 'keyboard', 'mouse'],
     'quantity': [3, 12, 12, 10, 2, 0, 21, 0, 10, 16],
     'assigned': ['susan', 'jane', 'amy', 'ben', 'donald', 'evan', 'chris', 'fred', 'henry', 'george']
    })
+--------+----------+--------+--------+
|asset_id|asset_type|assigned|quantity|
+--------+----------+--------+--------+
|   10092|    laptop|   susan|     3.0|
|   39883|  mousepad|    jane|    12.0|
|   82883|     mouse|     amy|    12.0|
|   28883|       pen|     ben|    10.0|
|   09093|       pen|  donald|     2.0|
|   27817|   monitor|    evan|     0.0|
|   38261|      wire|   chris|    21.0|
|   28372|    laptop|    fred|     0.0|
|   38472|  keyboard|   henry|    10.0|
|   39738|     mouse|  george|    16.0|
+--------+----------+--------+--------+

Я хотел бы применить фильтр, который возвращает строки, количество которых превышает указанное в следующем словаре количество (т. Е. Клавиатуры с количеством> = 10, провод с количеством> = 10, ноутбук с количеством> = 1 и т. Д.). Я также хотел бы вернуть строки, которые не содержат тип ресурса, указанный в словаре.

d = {'keyboard': 10, 'wire': 20, 'laptop': 1, 'mouse': 15, 'monitor': 1}

Вывод должен быть

+--------+----------+--------+--------+
|asset_id|asset_type|assigned|quantity|
+--------+----------+--------+--------+
|   10092|    laptop|   susan|     3.0|
|   38261|      wire|   chris|    21.0|
|   39738|     mouse|  george|    16.0|
|   38472|  keyboard|   henry|    10.0|
|   39883|  mousepad|    jane|    12.0|
|   28883|       pen|     ben|    10.0|
|   09093|       pen|  donald|     2.0|
+--------+----------+--------+--------+

Есть ли элегантный способ сделать это? Я реализовал вышеупомянутое, отфильтровывая и создавая фрейм данных для каждого элемента в словаре и добавляя к итоговому фрейму данных.

count = 0
for k, v in d.items():
    sdf_filter = sdf_assets.filter( (f.col('asset_type') == k) & (f.col('quantity') >= v) )
    if count != 0:
        sdf_thresholded = sdf_thresholded.union(sdf_filter)
    else:
        sdf_thresholded = sdf_filter
    count += 1

sdf_result_test = sdf_thresholded.union(sdf_assets.filter(~f.col('asset_type').isin(d.keys())))

Я написал все в PySpark, потому что в полном наборе данных много записей, но я был бы признателен за любое решение в python!

1 Ответ

0 голосов
/ 05 апреля 2019

Сначала преобразуйте ваш словарь d в искровой DataFrame:

d = {'keyboard': 10, 'wire': 20, 'laptop': 1, 'mouse': 15, 'monitor': 1}
d = spark.createDataFrame(d.items(), ["asset_type", "min_quantity"])
d.show()
#+----------+------------+
#|asset_type|min_quantity|
#+----------+------------+
#|    laptop|           1|
#|      wire|          20|
#|     mouse|          15|
#|   monitor|           1|
#|  keyboard|          10|
#+----------+------------+

Теперь вы можете ЛЕВО присоединиться к этому df_assets spark DataFrame и использовать where для фильтрации строк в соответствии с вашими условиями. Поскольку d невелик, вы можете использовать broadcast join для повышения производительности.

from pyspark.sql.functions import broadcast 

# first convert from pandas DataFrame to spark DataFrame
df_assets = spark.createDataFrame(df_assets)

# left join with d
df_assets.join(broadcast(d), on="asset_type", how="left")\
    .where("min_quantity IS NULL or quantity >= min_quantity")\
    .select("asset_id", "asset_type", "assigned", "quantity")\
    .show()
#+--------+----------+--------+--------+
#|asset_id|asset_type|assigned|quantity|
#+--------+----------+--------+--------+
#|   10092|    laptop|   susan|       3|
#|   39883|  mousepad|    jane|      12|
#|   28883|       pen|     ben|      10|
#|   09093|       pen|  donald|       2|
#|   38261|      wire|   chris|      21|
#|   38472|  keyboard|   henry|      10|
#|   39738|     mouse|  george|      16|
#+--------+----------+--------+--------+
...