Умная индексация по времени в пандах - PullRequest
0 голосов
/ 15 мая 2018

У меня есть такой фрейм данных:

| Date    | Device | Status |
| 1990/01 | 50     | ON     |
| 1990/01 | 20     | ON     |
| 1990/03 | 25     | ON     |
| 1990/05 | 50     | OFF    |
| 2000/01 | 20     | OFF    |

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

  1. df(device=50, date=1990/01) -> ON - легко
  2. df(device=50, date=1990/02) -> ON, для устройства 50 не существует 1990/02, но когда для устройства задано определенное состояние, оно остается в нем, если нет изменений
  3. df(device=50, date=1990/05) -> OFF
  4. df(device=50, date=2000/09) -> OFF
  5. df(device=50, date=1900/01) -> OFF, это сложный вопрос, перед первым включением устройство находится в состоянии ВЫКЛ.

Второй случай с разными статусами, но для краткости я оставлю его в том же духе. Поведение точно такое же, за исключением пункта 5. Когда мы предполагаем, что устройство до первого зарегистрированного состояния ВКЛ также находится в состоянии ВКЛ. Может быть больше статусов, но логика для определения статуса устройства точно такая же.

Как это сделать по-пандастически?

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Скажите, что ваш исходный кадр данных называется status_df и что вы преобразовали столбец Date в тип datetime.date. Затем вы можете определить функцию и вызвать ее с вашим status_df в качестве первого параметра и желаемыми device и check_date

def get_status(df, device, check_date):
    # Filter only those entries for this device and with date <= check_date
    filtered_df = df[(df['Device']==device) & (df['Date']<=check_date)]
    # Now sort by date
    filtered_df.sort_values('Date', inplace=True)
    # Now the last entry of the filtered_df contains the active status at check_date
    # If it is empty choose a default values (say OFF)
    if len(filtered_df) < 1:
         return 'OFF'
    return filtered_df.iloc[-1]['Status']

UPDATE Если вы не хотите создавать новый фрейм данных, вы можете сделать это с помощью простого оператора try

try:
    return df[(df['Device']==device) & (df['Date']<=check_date)].sort_values('Date').iloc[-1]['Status']
except IndexError:
    return 'OFF'
0 голосов
/ 15 мая 2018

По моему мнению, должны быть определены все возможные даты для выбора в date_range, который используется для reindex из pivot ed DataFrame. NaN s заменяются сначала при прямом заполнении, а все первые значения NaN до OFF на fillna:

print (df)
      Date  Device Status
0  1990/01      50     ON
1  1990/01      20     ON
2  1990/03      25     ON
3  1990/05      50    OFF
4  1990/05      20    OFF <-changed for smaller output df

df['Date'] = pd.to_datetime(df['Date'])

rng = pd.date_range('1989-10-01', '1991-01-01', freq='MS')
df = df.pivot('Date','Device','Status').reindex(rng).ffill().fillna('OFF')
print (df)
Device       20   25   50
1989-10-01  OFF  OFF  OFF
1989-11-01  OFF  OFF  OFF
1989-12-01  OFF  OFF  OFF
1990-01-01   ON  OFF   ON
1990-02-01   ON  OFF   ON
1990-03-01   ON   ON   ON
1990-04-01   ON   ON   ON
1990-05-01  OFF   ON  OFF
1990-06-01  OFF   ON  OFF
1990-07-01  OFF   ON  OFF
1990-08-01  OFF   ON  OFF
1990-09-01  OFF   ON  OFF
1990-10-01  OFF   ON  OFF
1990-11-01  OFF   ON  OFF
1990-12-01  OFF   ON  OFF
1991-01-01  OFF   ON  OFF

Последнее, если необходимо исходный формат dates добавить strftime:

df.index = df.index.strftime('%Y/%m')
print (df)
Device    20   25   50
1989/10  OFF  OFF  OFF
1989/11  OFF  OFF  OFF
1989/12  OFF  OFF  OFF
1990/01   ON  OFF   ON
1990/02   ON  OFF   ON
1990/03   ON   ON   ON
1990/04   ON   ON   ON
1990/05  OFF   ON  OFF
1990/06  OFF   ON  OFF
1990/07  OFF   ON  OFF
1990/08  OFF   ON  OFF
1990/09  OFF   ON  OFF
1990/10  OFF   ON  OFF
1990/11  OFF   ON  OFF
1990/12  OFF   ON  OFF
1991/01  OFF   ON  OFF

EDIT:

Более общее решение:

def get_status(df, device, check_date):
    check_date = pd.to_datetime(check_date)
    df['Date'] = pd.to_datetime(df['Date'])

    rng = pd.date_range(df['Date'].min(), df['Date'].max(), freq='MS')
    df = df.pivot('Date','Device','Status').reindex(rng).ffill().fillna('OFF')
    #print (df)

    if check_date < df.index.min():
        return 'OFF'
    elif check_date > df.index.max():
        return df.loc[df.index[-1], device]
    else:
        return df.loc[check_date, device]

print (get_status(df, 50, '1990/01'))
#ON
print (get_status(df, 50, '1990/02'))
#ON
print (get_status(df, 50, '1990/05'))
#OFF
print (get_status(df, 50, '1990/09'))
#OFF
print (get_status(df, 50, '1900/01'))
#OFF
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...