Заполните недостающие данные в панды таймсерии


Я собирал данные с помощью смартфона. Телефон может работать от батареи или коллекция программного обеспечения может не, что приводит к недостающим данным.

Для этого примера скажем, я уже считать количество звонков в день calls_per_day от смартфона данные. Кроме дней, когда я вообще кому-то позвонить, я хочу рассмотреть те дни, когда я не. Для этого я должен учитывать время, когда телефон был зондирования и назначить ноль в этот день, если не было никаких звонков. Для того, чтобы знать, если телефон был чувствительный, у меня есть эталонный датчик, который записывает галочку каждые 25 секунд: sensed_time.

Приведенный ниже код делает то, что я описала. Во-первых, это группы sensed_time каждую минуту, час, день, маркировка в день 'почувствовал', если он собрал не менее 80% минут, по крайней мере, 80% каждой из 24 часов в сутки. Потом он просто фильтрует дней с calls_per_day которые помечены как sensed и заменяет значения NaN с 0.

import numpy as np
def count_per_minute(group):
    if group[group.columns[0]].count() == 0:
        return pd.Series({'count_per_minute':np.nan})
    else:
        return pd.Series({'count_per_minute':group[group.columns[0]].count()})

def label_sensed_days(group, hours_in_day, percentage_valid_hours, percentage_valid_minutes):
    sensed_hours_count = group['sensed_minutes'].loc[group['sensed_minutes'] > 60 * percentage_valid_minutes].count()

    if  sensed_hours_count > 24 * percentage_valid_hours:
        return pd.Series({'sensed_day':True, 'sensed_hours':sensed_hours_count})
    else:
        return pd.Series({'sensed_day':False, 'sensed_hours':sensed_hours_count})

# Create fake DF with the timestamps where the phone was sensing
index = pd.date_range("2018-01-01", "2018-01-03 23:59", freq='25S')
sensed_time = pd.DataFrame(index=index, columns=['was_sensed'])
sensed_time = sensed_time.fillna(1)
sensed_time = sensed_time.sample(frac=0.6)

# Count records sensed per minute, return nan if count == 0
sensed_minutes = sensed_time.groupby(pd.Grouper(freq='1Min')).apply(count_per_minute)

# Complete missing minutes
sensed_minutes = sensed_minutes.reindex(pd.date_range(sensed_time.index.min().date(), sensed_time.index.max().date() + pd.DateOffset(1), freq='1Min'))

# Group sensed minutes by hour
sensed_hours = sensed_minutes.groupby([pd.Grouper(freq='1H')]).count()
sensed_hours = sensed_hours.rename(columns={'count_per_minute':'sensed_minutes'})

# Group sensed hours per day but only consider a valid day the ones where at least 0.8 percent of 24 hours were sensed with at least 0.8 percent of minutes
sensed_days = sensed_hours.groupby([pd.Grouper(freq='1D')]).apply(label_sensed_days, hours_in_day=24, percentage_valid_hours= 0.8, percentage_valid_minutes=0.8)

# Create fake DF with the number of calls on every other day
index = pd.date_range("2018-01-01", "2018-01-05", freq='2D')
calls_per_day = pd.DataFrame([10,5,8], index=index, columns=['calls'])

# Only keep the days that we consider valid sensed days
calls_per_day = calls_per_day.reindex(sensed_days[sensed_days['sensed_day'] == True].index)

# All the NaN values mean that the phone was sensing but we didn't record a call, thus there were 0 calls.
calls_per_day = calls_per_day.fillna(0)

display(calls_per_day)


207
3
задан 13 марта 2018 в 03:03 Источник Поделиться
Комментарии
1 ответ

Не забывайте, что вы можете цепи операций (кстати, вы пропустили панды импорт):

sensed_time = pd.DataFrame(index=index, columns=['was_sensed'])
sensed_time = sensed_time.fillna(1)
sensed_time = sensed_time.sample(frac=0.6)

x = pd.DataFrame(index=index, columns=['was_sensed']).fillna(1).sample(frac=0.6)

Вы можете проверить, если это работает путем делать sensed_time.shape == x.shape

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

Например, что-то вдоль линий:

    class SmartPhoneData(object):
def __init__(self, name=None):
self.name = name or "Nokia6_32GB"
self.data = pd.DataFrame(index=index, columns=['was_sensed']).fillna(1).sample(frac=0.6)

def calls_per_day(self):
print(f"SmartPhone Data: {self.data}")

@property
def minutes(self):
return self.data.reindex(pd.date_range(sensed_time.index.min().date(), sensed_time.index.max().date() + pd.DateOffset(1), freq='1Min'))

@property
def hours(self):
return self.data.groupby([pd.Grouper(freq='1H')]).count()

@property
def days(self):
return self.data.groupby([pd.Grouper(freq='1D')]).apply(label_sensed_days, hours_in_day=24, percentage_valid_hours=0.8, percentage_valid_minutes=0.8)

your_phone = SmartPhoneData()
my_phone = SmartPhoneData("iPhone X")

your_phone.calls_per_day()
my_phone.calls_per_day()

Очевидно, я просто взял одну строку в коде, где он был аналогичным названием (чтобы соответствовать функции).
Так что если вы это сделаете - убедитесь, что вы знаете, как получает данные изменены при выполнении каждой функции. Например, каждый из @properties просто не вернуть и не изменить таблицы данных, однако некоторые из ваших вариантов на самом деле может изменить данные содержимое (даже если нет назначения (напр.: x = a + b не меняет A или B, но (не реальный код) self.data(column_names = ["one", "two"]) будем его менять). Я надеюсь, что имело смысл?

Удачи!

1
ответ дан 14 марта 2018 в 03:03 Источник Поделиться