2016-02-19 2 views
2

Я работаю с данными, поступающими от датчика положения, который сообщает в угловых координатах (шаг, рулон и рыскание). Показания датчиков таковы, что максимальное показание составляет 180 градусов. Проходя мимо этого, показания переходят на отрицательные градусы. Например, слегка колеблющийся ряд показаний может пойти что-то вроде:Обработка обертывания значений в pandas

178.8 
178.9 
-178.8 
-178.0 
-179.0 
180.0 
179.0 

Я в настоящее время обработки, проверив все значения последовательно в цикле. Я получаю дельта между двумя последовательными показаниями и сохраняю текущую сумму, чтобы получить текущее угловое положение. Если я перехожу из третьего квадранта (90 < = предыдущее значение < = 180) в четвертое (-180 < = текущее значение < = -90), я добавляю 360 к текущему значению, прежде чем принимать дельту. Если я поеду четвертый квадрант в 3-й, я вычитаю 360, прежде чем принимать дельту. Например:

Yaw = inData['Yaw'] 
    cum_sum = Yaw[0] 
    for i in range(1, Yaw)): 
     x_prev = Yaw[i-1] 
     x = Yaw[i] 
     if 90 <= x_prev and x_prev <= 180 and -180 <= x and x <= -90: 
      x = x + 360 
     elif -180 <= x_prev and x_prev <= -90 and 90 <= x and x <= 180: 
      x = x - 360 
     else: 
      pass 
     delta = x - x_prev 
     cum_sum += delta 

Здесь входных данных, является dataframe, содержащий угловые положения из нескольких осей, и «рыскание» представляет собой серию в этом dataframe.

Если бы я мог выполнить последовательную поправку к значениям угла, я мог бы создать серию дельт и сделать кумулятивную сумму, чтобы получить позицию. Однако добавление 360 ко всем показаниям или работа с углами по модулю 360 только перемещает точку перевала с пересечения между +/- 180 и пересечением между 0 и 360.

Учитывая, что у меня уже есть эти показания в DataFrame/Series, мне было интересно, есть ли более Pythonic (и более быстрый) способ сделать это.

+0

Почему вы не добавить 360 градусов только значения, которые меньше нуля? – BrenBarn

+0

Что-то вроде: 'inData.Yaw [inData.Yaw <0.0] + = 360.' – screenpaver

+0

Обратите внимание, что это также работает и более читаемо:' -180 <= x <= -90'. Учитывая, что -180 является показанием min, вы можете просто использовать 'x <= -90'. Но я все равно не буду делать этот расчет в цикле. Посмотрите на мое векторное решение ниже. – Alexander

ответ

1

Все в векторе, поэтому должно быть быстро.

s = pd.Series([-178.8, 178.9, -178., -179., 180., 179.]) 

delta = (s.diff() + 
     (((s.shift() >= 90) & (s <= -90)).astype(int) - 
      ((s.shift() <= -90) & (s >= 90)).astype(int)) * 360) 

>>> delta.cumsum() 
0 NaN 
1 -2.3 
2 0.8 
3 -0.2 
4 -1.2 
5 -2.2 
dtype: float64 

Что касается логики, ((s.shift() >= 90) & (s <= -90)).astype(int) будет оценивать до 1, если измерение находится в верхнем левом квадранте и ноль в противном случае. -((s.shift() <= -90) & (s >= 90)).astype(int) будет оцениваться до минус один, если измерение находится в нижнем правом квадранте и ноль в противном случае. Это векторный способ сказать, что вы должны добавить 360 к разнице, если вы находитесь в верхнем левом квадранте и вычитаете 360 из разницы, если вы находитесь в правом нижнем квадранте, иначе просто возьмите разницу.

+0

Буду честным: у меня проблемы с логикой. Кроме того, эти ответы неверны. Дельты будут NaN, -2,3, 3,1, -1, -1, -1. –

+0

Извините, пришлось отложить в сторону на несколько минут. –

+0

Это кумулятивные дельта. Если вы добавите свои дельта выше, вы увидите, что результат идентичен. – Alexander

0

Если я вас понимаю хорошо, квадрант, как это (происходит по часовой стрелке):

  • первый: -90 до 0 градусов.
  • 2-й: от 0 до 90 градусов
  • 3-й: от 90 до 180 градусов.
  • 4-й: от -180 до 90 градусов.

Если это так, то я предлагаю вам первый преобразит ваши лекции обычных измерений 360 градусов:

def parseSensorReading(readingDegrees): 
    if readingDegrees < 0: 
     degrees360 = readingDegrees + 360 
    degrees360 = readingDegrees 
    return degrees360 

Затем обнаружить квадрант вы читаете из:

def getQuadrant(degrees360): 
    if degrees360 < 360: 
     quadrant = 1 
    if degrees360 < 270: 
     quadrant = 4 
    if degrees360 < 180: 
     quadrant = 3 
    if degrees360 < 90: 
     quadrant = 2 
    return quadrant 

Имея квадранты, теперь вы можете рассчитать свои дельта:

def getDelta(x_old, x_new): 
    q_old = getQuadrant(x_old) 
    q_new = getQuadrant(x_new) 
    if q_old == 1 and q_new == 2: 
     delta = (x_new + 360) - x_old 
    else: 
     delta = x_new - x_old 

И, наконец, ваш код становится:

yaw = inData["Yaw"] 
cumSum = yaw[0] 
for i in range(1, len(yaw) + 1)): 
    x_old = parseSensorReading(yaw[i-1]) 
    x_new = parseSensorReading(yaw[i]) 
    delta = getDelta(x_old, x_new) 
    cumSum += delta 

Примечание: Я изменил выражение «Диапазон» для цикла, как это было с помощью списка «рыскания» в качестве второго параметра. Я предполагаю, что вы хотели сказать len (yaw) + 1, поэтому «i» получает значения от 1 до len (yaw).

+0

Спасибо! Однако я подсчитывал квадранты против часовой стрелки. –

+0

Возможно, я неправильно сформулировал в своей первоначальной постановке проблемы. Сожалею. Кроме того, это, похоже, не подвержено векторизации. Я пытаюсь найти способ использования операций с numpy/pandas vector. –

0

Я думаю, np.unwrap() делает то, что вы хотите, без необходимости конфликтовать с дельтами и cumsum. Docs

In [120]: data = pd.Series([178.8, 178.9, -178.8, -178.0, -179.0, 180.0, 179.0]) 

In [121]: np.degrees(np.unwrap(np.radians(data))) 
Out[121]: array([ 178.8, 178.9, 181.2, 182. , 181. , 180. , 179. ]) 

Он обрабатывает несколько обходных обруча:

In [124]: np.degrees(np.unwrap(np.radians(data + 540))) 
Out[124]: array([ 718.8, 718.9, 721.2, 722. , 721. , 720. , 719. ]) 
+0

Умный! Честно говоря, np.unwrap() - одна из тех функций, которых я не знал. –

 Смежные вопросы

  • Нет связанных вопросов^_^