PySpark - эффективный способ найти столбцы DataFrame с более чем одним отдельным значением - PullRequest
0 голосов
/ 12 апреля 2019

Мне нужен эффективный способ перечисления и удаления одинарных столбцов в Spark DataFrame (я использую PySpark API).Я определяю унарный столбец как столбец, который имеет не более одного отдельного значения, и для целей определения я также считаю null значением.Это означает, что столбец с одним отдельным значением non-null в некоторых строках и null в других строках не является унарным столбцом.

На основании ответов на на этот вопрос мне удалосьнаписать эффективный способ получить список пустых столбцов (которые являются подмножеством моих унарных столбцов) и отбросить их следующим образом:

counts = df.summary("count").collect()[0].asDict()
null_cols = [c for c in counts.keys() if counts[c] == '0']
df2 = df.drop(*null_cols)

Исходя из моего очень ограниченного понимания внутренней работы Spark, этобыстро, потому что сводка метода управляет всем фреймом данных одновременно (у меня есть приблизительно 300 столбцов в моем начальном DataFrame).К сожалению, я не могу найти аналогичный способ для работы со вторым типом унарных столбцов, которые не имеют значений null, но имеют значение lit(something).

В настоящее время у меня есть следующее (используя df2, полученный из фрагмента кода выше):

prox_counts = (df2.agg(*(F.approx_count_distinct(F.col(c)).alias(c)
                         for c in df2.columns
                         )
                       )
                  .collect()[0].asDict()
               )
poss_unarcols = [k for k in prox_counts.keys() if prox_counts[k] < 3]
unar_cols = [c for c in poss_unarcols if df2.select(c).distinct().count() < 2]

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

Что мне не нравится в этом, так это то, что а) даже при предварительном предварительном отборе он все еще довольно медленный, захватывая всеминута бега, хотя на данный момент у меня всего около 70 столбцов (и около 6 миллионов строк) и б) я использую approx_count_distinct с магической постоянной 3 (approx_count_distinct не считается null, следовательно3 вместо 2).Поскольку я не совсем уверен, как внутренне работает approx_count_distinct, я немного обеспокоен тем, что 3 не особенно хорошая константа, поскольку функция может оценивать количество различных (non-null) значений, как, скажем, 5, когда это действительноравно 1, и поэтому, возможно, требуется более высокая константа, чтобы гарантировать, что в списке кандидатов ничего не пропущено poss_unarcols.

Есть ли более разумный способ сделать это, в идеале, чтобы мне даже не пришлось отбрасыватьобнулить столбцы отдельно и сделать все это одним махом (хотя это на самом деле довольно быстро и так большая проблема)?

Ответы [ 3 ]

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

вы можете df.na.fill («некоторое несуществующее значение»). Summary (), а затем удалить соответствующие столбцы из исходного кадра данных

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

На данный момент лучшее решение, которое я нашел, - это (оно быстрее, чем другие предложенные ответы, хотя и не идеально, см. Ниже):

rows = df.count()
nullcounts = df.summary("count").collect()[0].asDict()
del nullcounts['summary']
nullcounts = {key: (rows-int(value)) for (key, value) in nullcounts.items()}

# a list for columns with just null values
null_cols = []
# a list for columns with no null values
full_cols = []

for key, value in nullcounts.items():
    if value == rows:
        null_cols.append(key)
    elif value == 0:
        full_cols.append(key)

df = df.drop(*null_cols)

# only columns in full_cols can be unary
# all other remaining columns have at least 1 null and 1 non-null value
try:
    unarcounts = (df.agg(*(F.countDistinct(F.col(c)).alias(c) for c in full_cols))
                    .collect()[0]
                    .asDict()
                  )
    unar_cols = [key for key in unarcounts.keys() if unarcounts[key] == 1]
except AssertionError:
    unar_cols = []

df = df.drop(*unar_cols)

Это работает достаточно быстро, в основном потому, что у меня не так уж много "полных столбцов", то есть столбцов, которые не содержат null строк, и я только перебираю все эти строки, используя быстрый метод summary("count") для классификации столько столбцов, сколько я могу.

Прохождение всех строк столбца мне кажется невероятно расточительным, поскольку, как только найдены два разных значения, мне все равно, что находится в остальной части столбца. Я не думаю, что это можно решить в pySpark, хотя (но я новичок), для этого требуется UDF, а pySpark UDF настолько медленны, что вряд ли будут быстрее, чем countDistinct(). Тем не менее, до тех пор, пока в кадре данных есть много столбцов без null строк, этот метод будет довольно медленным (и я не уверен, насколько можно доверять approx_count_distinct(), чтобы различать одно или два различных значения в столбце )

Насколько я могу сказать, он превосходит подход collect_set(), и заполнение значений null на самом деле не является необходимым, как я понял (см. Комментарии в коде).

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

Я предлагаю вам взглянуть на следующую функцию

pyspark.sql.functions.collect_set(col)

https://spark.apache.org/docs/latest/api/python/pyspark.sql.html?highlight=dataframe

Она вернет все значения в столбце с удаленными умноженными элементами.Затем вы можете проверить длину результата (равен ли он одному).Мне было бы интересно узнать о производительности, но я думаю, что она определенно побьет отличную (). Count ().Давайте посмотрим в понедельник :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...