Понимание .groupby (). First () при сжатии Pandas DataFrame? - PullRequest
1 голос
/ 11 апреля 2020

Поскольку я не уверен в точной терминологии - скажем, у меня есть этот файл:

dataA.csv:

event,car,bike,bus
63175,,18,
65641,45,9,
65805,,,54
68388,,65,
68388,,,39
73041,7,,18
79336,,44,
79423,,,5

Считая это с помощью dataA = pd.read_csv("dataA.csv", dtype='Int64'), мы получаем a pandas DataFrame:

dataA:
   event   car  bike   bus
0  63175  <NA>    18  <NA>
1  65641    45     9  <NA>
2  65805  <NA>  <NA>    54
3  68388  <NA>    65  <NA>
4  68388  <NA>  <NA>    39
5  73041     7  <NA>    18
6  79336  <NA>    44  <NA>
7  79423  <NA>  <NA>     5

В двух строках столбец «событие» имеет одинаковое значение (то, что я называю «дубликатами»):

3  68388  <NA>    65  <NA>
4  68388  <NA>  <NA>    39

... и я бы хотел, чтобы они были «сжаты» (это правильное слово?) в одну строку, так что вместо NaN (то есть, NA) есть фактические значения (где это возможно):

3  68388  <NA>    65    39

От Как сжать объединение двух Pandas Данных с NaN и дублирующихся ключей соединения? Я получил ответ, что должен использовать .groupby(...).first() - и действительно, это работает; этот сценарий:

#!/usr/bin/env python3

import pandas as pd
print(pd.__version__) # 1.0.2 for me

dataA = pd.read_csv("dataA.csv", dtype='Int64')
print("dataA:")
print(dataA)

# make sure Pandas prints entirety of DataFrame
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)

dataCompact = dataA.groupby('event').first() ##***
print("\ndataCompact:")
print(dataCompact)

в конечном итоге печатает:

dataCompact:
        car  bike   bus
event
63175  <NA>    18  <NA>
65641    45     9  <NA>
65805  <NA>  <NA>    54
68388  <NA>    65    39
73041     7  <NA>    18
79336  <NA>    44  <NA>
79423  <NA>  <NA>     5

... что я и хотел, так что это работает.

Однако, при ближайшем рассмотрении, Я понял, что на самом деле не понимаю, как это работает, то есть я не могу точно сказать, на что конкретно ссылается .first() в данном случае; и https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.first.html мне не очень помогает, потому что в нем говорится " Метод для подстановки начальных периодов данных временных рядов на основе смещения даты. " (и большинство других вводных страниц в Интернете следуют пример дат), - но здесь я не использую даты.

Итак, я провел несколько экспериментов - в основном изменил строку, отмеченную ##*** в приведенном выше коде, и посмотрел на распечатки.

Во-первых, если я вместо этого использую эту строку:

dataCompact = dataA.groupby('event').apply(lambda x: "{} ({}): {}".format(type(x), len(x), x.values.tolist())) ##***

... Я получаю эту распечатку:

dataCompact:
event
63175                             <class 'pandas.core.frame.DataFrame'> (1): [[63175, <NA>, 18, <NA>]]
65641                                <class 'pandas.core.frame.DataFrame'> (1): [[65641, 45, 9, <NA>]]
65805                             <class 'pandas.core.frame.DataFrame'> (1): [[65805, <NA>, <NA>, 54]]
68388    <class 'pandas.core.frame.DataFrame'> (2): [[68388, <NA>, 65, <NA>], [68388, <NA>, <NA>, 39]]
73041                                <class 'pandas.core.frame.DataFrame'> (1): [[73041, 7, <NA>, 18]]
79336                             <class 'pandas.core.frame.DataFrame'> (1): [[79336, <NA>, 44, <NA>]]
79423                              <class 'pandas.core.frame.DataFrame'> (1): [[79423, <NA>, <NA>, 5]]
dtype: object

Отсюда я понимаю, что, по сути, groupby('event') обеспечивает DataFrame для каждого уникального значения столбца 'event':

  • , если значение уникально в исходном наборе данных, этот DataFrame имеет одну строку; однако
  • если это «дублирующее» значение, мы получаем DataFrame с двумя строками (или столько же строк, сколько имеется «дубликатов» этого конкретного значения).

Таким образом, .first() должен принять DataFrame с N> = 1 строками в качестве входных данных и вернуть одну строку.

Однако здесь начинается мое замешательство - я бы прочитал .first(), чтобы сослаться на возвращение первые из N> = 1 входных строк; но в этом случае значения не были бы «сжаты» (то есть слоты с действительными номерами заменяют слоты на <NA> (неопределенные значения)); - вместо этого все остальные строки, кроме первого, будут удалены! И это не то, что здесь происходит ...

Итак, я попытался смоделировать то, что делает .first(), написав свой собственный лямбда-обработчик для .apply():

def proc_df_first(x):
  # here we get a DataFrame with single row, if "event" (groupby arg) is a unique value;
  # or a DataFrame with as many rows, as there are repeated rows with "event" of same value ("duplicate")
  if len(x) == 1:
    return x
  elif len(x) > 1:
    # create empty return DataFrame (eventually it will only have a single row)
    retdf = pd.DataFrame(columns = x.columns)
    #return retdf # is empty, so is skipped in final result of .groupby.apply
    # must populate rowdata first, then assign via .loc (SO:17091769)
    for icol in x.columns:
      coldata = x[icol] # is Series
      thisval = pd.NA # initialize the "compact" single value we want to set for this column (eventually derived from all the row values in this column)
      for idx, val in coldata.iteritems():
        #print("***", idx, val, val is pd.NA) # `val is None` is always False; `val==pd.NA` always prints `<NA>`; `val is pd.NA` is correct
        if thisval is pd.NA:
          if val is not pd.NA:
            # found the first non-NA value; save it, and stop looking further
            thisval = val
            break
      # store saved non-NA value into return DataFrame
      retdf.at[ x.index[0], icol ] = thisval # SO: 13842088
    return retdf

dataCompact = dataA.groupby('event').apply(lambda x: proc_df_first(x)) ##***

.. ... который в итоге печатает:

dataCompact:
         event   car  bike   bus
event
63175 0  63175  <NA>    18  <NA>
65641 1  65641    45     9  <NA>
65805 2  65805  <NA>  <NA>    54
68388 3  68388   NaN    65    39
73041 5  73041     7  <NA>    18
79336 6  79336  <NA>    44  <NA>
79423 7  79423  <NA>  <NA>     5

... что по сути тот же результат, что и .groupby('event').first() (кроме дублированного столбца "события" (это будет иерархическая метка?) и индекса столбец).

Итак, вот мои вопросы:

  • В соответствии с вышесказанным, я бы сказал, что .first() возвращает первое не-NA значение для каждого столбца в набор строк, приводящий к однорядному представлению набора строк - это правильно?
  • Почему, из-за громкого крика, я получил NaN в столбце "car" вывода для события " "== 68388 - когда я попытался обработать все внутренне как" Int64 "(с соответствующим значением pd.NA)? Я знаю, что могу сделать dataA.groupby('event').apply(lambda x: proc_df_first(x)).astype('Int64') и получить pd.NA везде - но, учитывая, что я перебираю всю таблицу элемент за элементом, я бы не хотел, чтобы еще одна l oop по таблице - просто чтобы получить избавиться от одного поплавка NaN. Что я мог сделать в своем обработчике, чтобы гарантировать, что возврат из proc_df_first() в .apply() всегда будет иметь значения pd.NA, если это конкретное значение не определено?

1 Ответ

2 голосов
/ 11 апреля 2020

В pandas есть 2 разные first функции, которые различны:

GroupBy.first и DataFrame.first.

Здесь сначала используется first для групп.

В соответствии с вышесказанным, я бы сказал, что .first () возвращает первое не-NA значение для каждого столбца в наборе строк , приводя к однорядному представлению набора строк - это правильно?

Да, вы правы, но это что-то вроде долгосрочной ошибки, 6732 и 8427 . До сих пор в pandas 1.0.1 .

Почему, из-за громкого крика, я получил NaN в столбце "car" вывода для "event" == 68388

К сожалению, я думаю, потому что ошибка.

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