Pandas Timedelta mean возвращает ошибку «Нет числовых типов для агрегирования». Почему? - PullRequest
0 голосов
/ 09 октября 2019

Я пытаюсь выполнить следующую операцию:

pd.concat([A,B], axis = 1).groupby("status_reason")["closing_time"].mean()

Где

  • A - это серия с именем "status_reason" (Категориальные значения)
  • Bявляется серией с именем «Closing_time» (значения TimeDelta)

Пример:

In : A.head(5)
Out: 
     0    -1 days +11:35:00
     1   -10 days +07:13:00
     2                  NaT
     3                  NaT
     4                  NaT
    Name: closing_time, dtype: timedelta64[ns]

In : B.head(5)
Out:
     0            Won
     1       Canceled
     2    In Progress
     3    In Progress
     4    In Progress
     Name: status_reason, dtype: object

Произошла следующая ошибка:

DataError: No numeric types to aggregate

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

Теперь я увидел несколько вопросов, похожих на мой онлайн, поэтому я попробовал это:

pd.to_timedelta(pd.concat([pd.to_numeric(A),B], axis = 1).groupby("status_reason")["closing_time"].mean())

, который просто конвертирует Timedelta в int64и наоборот. Но результат был довольно странный (слишком большое число)

Чтобы исследовать ситуацию, я написал следующий код:

xxx = pd.concat([A,B], axis = 1)
xxx.closing_time.mean()
#xxx.groupby("status_reason")["closing_time"].mean()

Второй ряд РАБОТАЕТ КАЧЕСТВЕННО, без преобразования Timedelta вInt64. Третья строка НЕ ​​работает и возвращает снова DataError.

Я так запутался здесь! Что я пропустил?

Я хотел бы видеть среднее значение "времени закрытия" для каждой "причины состояния"!

РЕДАКТИРОВАТЬ

Если я попытаюсь сделать это: (изолировать строки с определенным статусом без группировки)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy["closing_time"].mean()

В результате:

Timedelta('310 days 21:18:05.454545')

Но если я сделаю это: (Изолировать строкис определенной группировкой статуса)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.groupby("status_reason")["closing_time"].mean()

Результат снова:

DataError: No numeric types to aggregate

Наконец, если я сделаю это: (преобразование и преобразование обратно) (ДАВАЙТЕ ПОЗВОНИТЬ ЭТО: Специальный пример )

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.closing_time = pd.to_numeric (yyy.closing_time)
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean())

Вернемся к первой замеченной мной проблеме:

status_reason
In Progress   -105558 days +10:08:05.605064
Name: closing_time, dtype: timedelta64[ns]

EDIT2

Если я это сделаюthis: (преобразовать в секунды и преобразовать обратно)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.closing_time = A.dt.seconds
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean(), unit="s" )

Результат будет

status_reason
In Progress   08:12:38.181818
Name: closing_time, dtype: timedelta64[ns]

Тот же результат произойдет, если я удалю NaN или заполню их 0:

yyy = xxx[xxx["status_reason"] == "In Progress"].dropna()
yyy.closing_time = A.dt.seconds
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean(), unit="s" )

НО цифры сильно отличаются от того, что мы видели в первом редактировании! ( Специальный пример )

-105558 days +10:08:05.605064

Кроме того, позвольте мне запустить тот же код ( Специальный пример ) с dropna ():

310 days 21:18:05.454545

И снова, давайте запустим тот же код ( Специальный пример ) с fillna (0):

3 days 11:14:22.819472

Это ни к чему не приведет. Я, вероятно, должен подготовить экспорт этих данных и выложить их куда-нибудь: Вот и мы

Ответы [ 4 ]

1 голос
/ 09 октября 2019

Я не могу сказать, почему метод groupby mean () не работает, но следующая небольшая модификация вашего кода должна работать: сначала преобразуйте столбец timedelta в секунды с помощью метода total_seconds (), затем groupby и mean, затем конвертируйте секунды в timedeltaеще раз:

pd.to_timedelta(pd.concat([ A.dt.total_seconds(), B], axis = 1).groupby("status_reason")["closing_time"].mean(), unit="s")

Например, приведенный ниже кадр данных, код -

df = pd.DataFrame({'closing_time':['2 days 11:35:00', '07:13:00', np.nan,np.nan, np.nan],'status_reason':['Won','Canceled','In Progress', 'In Progress', 'In Progress']})

df.loc[:,"closing_time"] = \
          pd.to_timedelta(df.closing_time).dt.days*24*3600 \
          + pd.to_timedelta(df.closing_time).dt.seconds

# or alternatively use total_seconds() to get total seconds in timedelta as follows
# df.loc[:,"closing_time"] = pd.to_timedelta(df.closing_time).dt.total_seconds()

pd.to_timedelta(df.groupby("status_reason")["closing_time"].mean(), unit="s")

производит

status_reason
Canceled      0 days 07:13:00
In Progress               NaT
Won           2 days 11:35:00
Name: closing_time, dtype: timedelta64[ns]
1 голос
/ 09 октября 2019

Прочитав обсуждение этой проблемы на Github здесь , вы можете решить эту проблему, указав numeric_only = False для вычисления среднего значения следующим образом

pd.concat([A,B], axis = 1).groupby("status_reason")["closing_time"] \
    .mean(numeric_only=False)
1 голос
/ 09 октября 2019

Возможно, проблема In Progress имеет только NaT время, что может быть запрещено в groupby().mean(). Вот тест:

df = pd.DataFrame({'closing_time':['11:35:00', '07:13:00', np.nan,np.nan, np.nan],
                   'status_reason':['Won','Canceled','In Progress', 'In Progress', 'In Progress']})
df.closing_time = pd.to_timedelta(df.closing_time)
df.groupby('status_reason').closing_time.mean()

дает точную ошибку. Чтобы преодолеть это, выполните:

def custom_mean(x):
    try:
        return x.mean()
    except:
        return pd.to_timedelta([np.nan])

df.groupby('status_reason').closing_time.apply(custom_mean)

, что дает:

status_reason
Canceled      07:13:00
In Progress        NaT
Won           11:35:00
Name: closing_time, dtype: timedelta64[ns]
0 голосов
/ 10 октября 2019

После нескольких исследований вот что я нашел:

Большая часть путаницы связана с тем, что в одном случае я вызывал SeriesGroupBy.mean (), а в другом - Series.mean ()

Эти функции на самом деле разные и имеют разное поведение. Я не осознавал, что

Вторым важным моментом является то, что преобразование в числовые или в секунды приводит к совершенно другому поведению, когда дело доходит до обработки значения NaN.

Чтобы преодолеть эту ситуацию,первое, что вам нужно сделать, это решить, как обрабатывать значения NaN. Лучший подход зависит от того, чего мы хотим достичь. В моем случае хорошо иметь даже простой категориальный результат, поэтому я могу сделать что-то вроде этого:

import datetime

def define_time(row):
    if pd.isnull(row["closing_time"]):
        return "Null"
    elif row["closing_time"] < datetime.timedelta(days=100):
        return "<100"
    elif row["closing_time"] > datetime.timedelta(days=100):
        return ">100"


time_results = pd.concat([A,B], axis = 1).apply(lambda row:define_time(row), axis = 1)

В итоге результат будет таким:

In : 
    time_results.value_counts()
Out : 
    >100    1452
    <100    1091
    Null    1000
    dtype: int64
...