Замените .withColumn на df.select

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

    def trim_and_lower_col(col_name):
        return F.when(F.trim(col_name) == "", F.lit("unspecified")).otherwise(F.lower(F.trim(col_name)))

    df = (
        source_df.withColumn("browser", trim_and_lower_col("browser"))
        .withColumn("browser_type", trim_and_lower_col("browser_type"))
        .withColumn("domains", trim_and_lower_col("domains"))
    )

Я читал, что создание нескольких операторов withColumn не очень эффективно, и вместо этого я должен использовать df.select(). Я пробовал это:

    cols_to_transform = [
    "browser",
    "browser_type",
    "domains"
    ]


    df = (
    source_df.select([trim_and_lower_col(col).alias(col) for col in cols_to_transform] + source_df.columns)
    )

но это дает мне повторяющуюся ошибку столбца

Что еще я могу попробовать?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
75
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы называете свои новые столбцы следующим образом: .alias(col). Это означает, что они имеют то же имя, что и столбец, который вы используете для создания нового.

При создании (используя .withColumn) это не представляет проблемы. Как только вы пытаетесь select, Spark не знает, какой столбец выбрать.

Вы можете исправить это, например, указав суффикс для новых столбцов:

cols_to_transform = [
"browser",
"browser_type",
"domains"
]


df = (
source_df.select([trim_and_lower_col(col).alias(f"{col}_new") for col in cols_to_transform] + source_df.columns)
)

Другим решением, которое действительно загрязняет DAG, будет:

cols_to_transform = [
"browser",
"browser_type",
"domains"
]

for col in cols_to_transform:
    source_df = source_df.withColumn(col, trim_and_lower_col(col))

нет ли способа просто «заменить» столбец? я не хочу менять имя

x89 07.02.2023 14:00

что-то вроде карты?

x89 07.02.2023 14:01

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

Robert Kossendey 07.02.2023 14:40
Ответ принят как подходящий

Дублирующийся столбец возникает из-за того, что вы дважды передаете каждый преобразованный столбец в этом списке, один раз как новый преобразованный столбец (через .alias) как исходный столбец (по имени в source_df.columns). Это решение позволит вам использовать один оператор select, сохранить порядок столбцов и не столкнуться с проблемой дублирования:

df = (
    source_df.select([trim_and_lower_col(col).alias(col) if col in cols_to_transform else col for col in source_df.columns])
)

Объединение множества .withColumn действительно создает проблему, поскольку неразрешенный план запроса может стать довольно большим и вызвать ошибку StackOverflow в драйвере Spark во время оптимизации плана запроса. Здесь можно найти одно хорошее объяснение этой проблемы: https://medium.com/@manuzhang/the-hidden-cost-of-spark-withcolumn-8ffea517c015

это дало бы мне ошибку, что: Объект функции не имеет псевдонима атрибута. Пожалуйста, проверьте написание и/или тип данных объекта.

x89 07.02.2023 15:30

это странно, я только что дважды проверил, что это работает в Code Authoring в Foundry. вы уверены, что нет опечатки?

proggeo 07.02.2023 15:32

Если у вас есть только эти несколько withColumns, продолжайте их использовать. Это все еще более читабельно, поэтому более удобно в обслуживании и не требует пояснений..

Если вы посмотрите на это, вы увидите, что искра говорит быть осторожным с withColumns, когда у вас их около 200.

Использование select также делает ваш код более подверженным ошибкам, поскольку его сложнее читать.

Теперь, если у вас много столбцов, я бы определил

  • список столбцов для преобразования,
  • список столбцов, которые необходимо сохранить
  • затем сделайте выбор
cols_to_transform = [
  "browser",
  "browser_type",
  "domains"
]
cols_to_keep = [c for c in df.columns if c not in cols_to_transform]
cols_transformed = [trim_and_lower_col(c).alias(c) for c in cols_to_transform]
source_df.select(*cols_to_keep, *cols_transformed)

Это даст вам тот же порядок столбцов, что и withColumns.

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