Pyspark разделяет столбец на его промежуточные итоги, сгруппированные по другому столбцу

Моя проблема похожа на это и это. Оба поста показывают, как разделить значение столбца на общую сумму того же столбца. В моем случае я хочу разделить значения столбца на сумму промежуточных итогов. Промежуточный итог рассчитывается путем группировки значений столбца в зависимости от другого столбца. Я немного изменяю пример, упомянутый в приведенных выше ссылках.

Вот мой фрейм данных

df = [[1,'CAT1',10], [2, 'CAT1', 11], [3, 'CAT2', 20], [4, 'CAT2', 22], [5, 'CAT3', 30]]
df = spark.createDataFrame(df, ['id', 'category', 'consumption'])
df.show()
+---+--------+-----------+
| id|category|consumption|
+---+--------+-----------+
|  1|    CAT1|         10|
|  2|    CAT1|         11|
|  3|    CAT2|         20|
|  4|    CAT2|         22|
|  5|    CAT3|         30|
+---+--------+-----------+

Я хочу разделить значение «потребления» на общее количество сгруппированных «категорий» и поместить значение в «нормализованный» столбец, как показано ниже.

Промежуточные итоги не обязательно должны быть в выводе (числа 21, 42 и 30 в потреблении столбца) Pyspark разделяет столбец на его промежуточные итоги, сгруппированные по другому столбцу

Что я достиг до сих пор df.crossJoin(

df.groupby('category').agg(F.sum('consumption').alias('sum_'))
).withColumn("normalized", F.col("consumption")/F.col("sum_"))\
.show()

+---+--------+-----------+--------+----+-------------------+
| id|category|consumption|category|sum_|         normalized|
+---+--------+-----------+--------+----+-------------------+
|  1|    CAT1|         10|    CAT2|  42|0.23809523809523808|
|  2|    CAT1|         11|    CAT2|  42| 0.2619047619047619|
|  1|    CAT1|         10|    CAT1|  21|0.47619047619047616|
|  2|    CAT1|         11|    CAT1|  21| 0.5238095238095238|
|  1|    CAT1|         10|    CAT3|  30| 0.3333333333333333|
|  2|    CAT1|         11|    CAT3|  30|0.36666666666666664|
|  3|    CAT2|         20|    CAT2|  42|0.47619047619047616|
|  4|    CAT2|         22|    CAT2|  42| 0.5238095238095238|
|  5|    CAT3|         30|    CAT2|  42| 0.7142857142857143|
|  3|    CAT2|         20|    CAT1|  21| 0.9523809523809523|
|  4|    CAT2|         22|    CAT1|  21| 1.0476190476190477|
|  5|    CAT3|         30|    CAT1|  21| 1.4285714285714286|
|  3|    CAT2|         20|    CAT3|  30| 0.6666666666666666|
|  4|    CAT2|         22|    CAT3|  30| 0.7333333333333333|
|  5|    CAT3|         30|    CAT3|  30|                1.0|
+---+--------+-----------+--------+----+-------------------+
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
1 842
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете сделать в основном то же самое, что и в ссылках, которые вы уже упомянули. Единственная разница в том, что вам нужно вычислить промежуточные итоги с помощью группа по и сумма:

import pyspark.sql.functions as F
df = df.join(df.groupby('category').sum('consumption'), 'category')
df = df.select('id', 'category', F.round(F.col('consumption')/F.col('sum(consumption)'), 2).alias('normalized'))
df.show()

Выход:

+---+--------+----------+ 
| id|category|normalized| 
+---+--------+----------+ 
|  3|    CAT2|      0.48| 
|  4|    CAT2|      0.52| 
|  1|    CAT1|      0.48| 
|  2|    CAT1|      0.52| 
|  5|    CAT3|       1.0| 
+---+--------+----------+ 

Это еще один способ решения проблемы, предложенный OP, но без использования joins().

joins() в целом являются дорогостоящими операциями, и их следует избегать, когда это возможно.

# We first register our DataFrame as temporary SQL view
df.registerTempTable('table_view')
df = sqlContext.sql("""select id, category, 
                       consumption/sum(consumption) over (partition by category) as normalize
                       from table_view""")
df.show()
+---+--------+-------------------+
| id|category|          normalize|
+---+--------+-------------------+
|  3|    CAT2|0.47619047619047616|
|  4|    CAT2| 0.5238095238095238|
|  1|    CAT1|0.47619047619047616|
|  2|    CAT1| 0.5238095238095238|
|  5|    CAT3|                1.0|
+---+--------+-------------------+

Примечание:""" использовался для многострочных операторов ради наглядности и аккуратности. С простым 'select id ....' это не сработает, если вы попытаетесь распространить свое утверждение на несколько строк. Излишне говорить, что конечный результат будет таким же.

Мне никогда не приходило в голову, что я могу использовать over и partition by - аккуратное решение

Ali 15.04.2019 01:17

Другие вопросы по теме