У меня есть фрейм данных, который содержит столбец высоты. Вначале высота остается неизменной с точки зрения колебаний сигнала GPS. В какой-то момент высота меняется, когда GPS-приемник перемещается во время подъема в гору. Как мне найти временную метку, когда высота начнет непрерывно перемещаться вверх? Простое сравнение «больше чем» не сработает, поскольку вначале во время разминки высота немного подпрыгивает.
Пример таймера здесь:
import pandas as pd
import numpy as np
df_GPS = pd.DataFrame([['2024-06-21 06:22:38', 605.968389],
['2024-06-21 06:22:39', 606.009398],
['2024-06-21 06:22:40', 605.630573],
['2024-06-21 06:22:41', 605.476367 ],
['2024-06-21 06:22:42', 605.322161],
['2024-06-21 06:22:43', 605.268389],
['2024-06-21 06:22:44', 605.559398],
['2024-06-21 06:22:45', 606.630573],
['2024-06-21 06:22:46', 607.476367 ],
['2024-06-21 06:22:47', 609.322161],
], columns=['time', 'Altitude'])
В идеале я бы получил в результате: время = 2024-06-21 06:22:43 так как оттуда высота монотонно возрастает.
Мой код все еще неисправен
start_move = df_GPS.loc[df_GPS['Altitude'] == df_GPS["Altitude"].is_monotonic_increasing, 'time']
print(start_move)
Где моя опечатка?
@user19077881 user19077881 ты имеешь в виду что-то вроде start_move = df_GPS.loc[df_GPS['Altitude'][::-1] == df_GPS["Altitude"].loc[::-1].is_monotonic_decreasing, 'time']
? Это тоже не сработает, поскольку, очевидно, я вернулся с горы, и трек тоже содержит данные о восхождении :-(
Я мог бы использовать пороговое значение для сравнения с предыдущими данными. Если разница между двумя наборами данных превышает пороговое значение, это мое время start_move. Очевидно, что таким образом трек начнется на несколько секунд позже...
что-то вроде этого было бы обходным решением, даже если это не совсем то решение: start_move = df_GPS.loc[df_GPS['Altitude'].diff(periods=1) > 0.5, 'time'].min()
Предполагая, что time
преобразован в datatime/timestamp, вы можете просто посмотреть назад с конца, чтобы найти изменение от монотонного. Предполагая, что данные времени необходимо отсортировать, сначала измените порядок:
df_GPS_rev = df_GPS.sort_values(by = 'time', ascending = False)
Альтернативно, если данные уже упорядочены по времени, то единственным требованием является изменение существующего порядка на противоположный, поэтому необходимо только переиндексировать:
df_GPS_rev = df_GPS.reindex(index = df_GPS.index[::-1])
затем
start_move = df_GPS_rev[df_GPS_rev['Altitude'] < df_GPS_rev['Altitude'].shift(-1)].iloc[0]
print(start_move['time'])
дает:
2024-06-21 06:22:43
Если (как позже определено в комментариях OP) данные НЕ всегда монотонно увеличиваются, а, например, уменьшаются в конце, то можно проверить скользящие окна на предмет монотонного увеличения:
# define window size
win= 3
# find windows which are monotonic increasing
df_GPS['win_inc'] = df_GPS.rolling(win)['Altitude'].apply(lambda x: x.is_monotonic_increasing)
#get index of end of first increasing window
idx = df_GPS[df_GPS['win_inc'].eq(True)].index[0]
# get time at start of first increasing window
start_move = df_GPS.loc[idx-win+1, 'time']
print(start_move)
что снова дает:
2024-06-21 06:22:43
Более быстрый, но менее лаконичный подход может использовать Numpy:
# define window size
win= 3
#create numpy array
alt = df_GPS['Altitude'].to_numpy()
#search through np array looking for first increasing window
start_idx = None
for idx, val in enumerate(alt[:1-win]):
if np.all(np.diff(alt[idx:idx+win]) > 0):
start_idx = idx
break
if start_idx is not None:
start_move = df_GPS.loc[start_idx, 'time']
print(start_move)
else:
print('no increasing windows')
что снова дает тот же результат
это может быть выходом, но манипулирование данными с точки зрения их сортировки всегда вызывает у меня головную боль... нет другого выхода без предварительной сортировки набора данных, верно?
См. отредактированный ответ: необходимо только переиндексировать, а не сортировать. Возможно, это лечит головную боль!
ну ок, это работает для наборов данных, которые просто растут. Полный набор данных содержит также данные о том, что я снова вернулся с горы, и здесь даже переиндексация не сработала. Следовательно, я принимаю ответ как решение, поскольку в примере я не знал, что мне нужно добавить спуск с горы, чтобы полностью решить мою проблему.
См. отредактированный ответ с подходом rolling window
, который касается данных, которые также спускаются позже. Надеюсь, теперь это будет полезно.
вау, круто, это работает для набора данных, содержащего подъем и спуск! Я бы оценил его еще раз, если бы мог. Большое спасибо за помощь в понимании программирования на Python. Часть .apply(lambda x: x.is_monotonic_increasing)
была той, о которой я не думал...
Хотя код краток, метод прокрутки Pandas может быть несколько неэффективным для огромных наборов данных, поскольку он находит все увеличивающиеся окна, прежде чем использовать только первое из них. У меня есть аналогичный, хотя и менее краткий подход, который следует той же логике, используя цикл Numpy для проверки только первого увеличивающегося окна — дайте мне знать, если это важно для вас.
да, ты прав. Для обработки больших наборов данных требуется некоторое время. Если существует более быстрое решение, которое прекращает расчет, как только происходит первое обнаружение, мне было бы любопытно узнать об этом. Вы имеете в виду решение с циклом for?
У него есть цикл for, но внутри массива Numpy, а не Pandas, и он предназначен только для поиска первого окна, следовательно, намного быстрее. Смотрите отредактированный ответ.
кажется, это более быстрый способ расчета. Следовательно, мне нужно как-то немного изменить одну строку, чтобы она работала и на моей машине: if start_idx is not None: start_move = df_GPS['time'][start_idx]
. Большой! Спасибо за этот приятный урок :-)
первое решение занимает около 0,585 с, второе — около 0,002 с, время обработки набора данных, содержащего 9486 строк записей данных — почти в 300 раз быстрее... Это потрясающе :-)
is_monotonic_increasing применяется ко всей серии высот, а не к ее части. Вы можете начать с последнего раза и вернуться назад в поисках разницы в высоте, указывающей на изменение.