Pyspark: как преобразовать часы с десятичной дробью в чч: мм - PullRequest
3 голосов
/ 27 мая 2020

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

# +----+----+--------+
# |col1|total_hours  |
# +----+-------------+
# |obj1| 48387.837   |
# |obj2| 45570.0201  |
# |obj3| 39339.669   |
# |obj4| 37673.235   |
# |obj5| 3576        |
# |obj6| 15287.9999  |
# +----+-------------+

Я хочу показать общее количество часов в формате часы: минуты.

желаемый результат :

# +----+----+--------+
# |col1|total_hours  |
# +----+-------------+
# |obj1| 48387:50    |
# |obj2| 45570:01    |
# |obj3| 39339:40    |
# |obj4| 37673:14    |
# |obj5| 3576:00     |
# |obj6| 15288:00    |
# +----+-------------+

в SQL Я могу сделать это с помощью следующей функции:

  hr = trunc(col1);
  minutes = round(hr -trunc(hr)* 0.6, 2);

  hours_minutes= trim(replace(to_char(hr + minutes ,'999999999990.90'),'.',':'));

Как это можно сделать в Pyspark?

Ответы [ 2 ]

3 голосов
/ 27 мая 2020

Это потребует манипуляций со строками, учитывая, что простое форматирование не работает. Это подбирает модификацию числа, умножает его на 60, форматирует оба и затем объединяет:

df.withColumn('total_hours_str', 
   f.concat(f.regexp_replace(f.format_number(f.floor(df.total_hours), 0), ',', ''), 
            f.lit(':'),  
            f.lpad(f.format_number(df.total_hours%1*60, 0), 2, '0'))).show()

Вывод:

+----+-----------+---------------+
|col1|total_hours|total_hours_str|
+----+-----------+---------------+
|obj1|  48387.837|       48387:50|
|obj2| 45570.0201|       45570:01|
|obj3|  39339.669|       39339:40|
|obj4|  37673.235|       37673:14|
|obj5|     3576.0|        3576:00|
+----+-----------+---------------+

EDIT: Поскольку у вас есть дробные значения, которые в конечном итоге округляются до целого часа, я предлагаю вам округлить перед обработкой столбца:

df.withColumn('rounded_total_hours', f.round(df['total_hours'],2))\
  .withColumn('total_hours_str', 
      f.concat(f.regexp_replace(f.format_number(f.floor(f.col('rounded_total_hours')), 0), ',', ''), 
               f.lit(':'),  
               f.lpad(f.format_number(f.col('rounded_total_hours')%1*60, 0), 2, '0'))).show()

Что дает:

+----+-----------+-------------------+---------------+
|col1|total_hours|rounded_total_hours|total_hours_str|
+----+-----------+-------------------+---------------+
|obj1|  48387.837|           48387.84|       48387:50|
|obj2| 45570.0201|           45570.02|       45570:01|
|obj3|  39339.669|           39339.67|       39339:40|
|obj4|  37673.235|           37673.24|       37673:14|
|obj5|     3576.0|             3576.0|        3576:00|
|obj6| 15287.9999|            15288.0|       15288:00|
+----+-----------+-------------------+---------------+
0 голосов
/ 27 мая 2020

Если желаемый тип данных является строкой, это можно сделать с помощью строки concat.

Шаги:

  1. Извлеките часы, создав столбец, приводящий total_hours к IntegerType()
  2. Извлеките долю часов, вычтя это значение из total_hours
  3. , умножьте это десятичное число на 60, чтобы получить количество минут,
  4. преобразовывает в строку и конкатенирует с разделителем :.

Код:

from pyspark.sql.types import IntegerType
from pyspark.sql.functions import concat_ws

df = df.withColumn('total_hour_int', df['total_hours'].cast(IntegerType())
df = df.withColumn('hours_remainder', df['total_hours']-df['total_hour_int'])
df = df.withColumn('minutes', df['hours_remainder']*60)
df = df.withColumn('minutes_full', df['minutes'].cast(IntegerType())
df = df.withColumn('total_hours_string', concat_ws(':', df['total_hour_int'], df['minutes_full'])
...