2012-05-07 3 views
5

У меня есть периодические данные с индексом является число с плавающей точкой, как так:Расчет пересечения (перехватывать) точки в серии или DataFrame

time = [0, 0.1, 0.21, 0.31, 0.40, 0.49, 0.51, 0.6, 0.71, 0.82, 0.93] 
voltage = [1, -1, 1.1, -0.9, 1, -1, 0.9,-1.2, 0.95, -1.1, 1.11] 
df = DataFrame(data=voltage, index=time, columns=['voltage']) 
df.plot(marker='o') 

Я хочу создать функцию cross(df, y_val, direction='rise' | 'fall' | 'cross'), который возвращает массив (индексы) со всеми интерполированными точками , где значения напряжения равны y_val. Для «подъем» возвращаются только значения, когда наклон положителен; для 'fall' только значения с отрицательным наклоном сохраняются; для 'крест' обе возвращены. Поэтому, если y_val = 0 и direction = 'cross', тогда массив с 10 значениями будет возвращен с значениями X точек пересечения (первый из которых составляет около 0,025).

Я думал, что это можно сделать с помощью итератора, но было интересно, есть ли лучший способ сделать это.

Спасибо. Я люблю Панд и сообщество Панд.

+0

Кстати, вы, возможно, наткнулись на ошибку в заговоре панд. Я считаю, что первое пересечение должно составлять около 0,05, исходя из данных, но метки не выстраиваются в линию, заставляя его пересечься на 0,025. (pandas 0.7.3) – Garrett

ответ

13

Для этого я получил следующее. Это векторная версия, которая в 150 раз быстрее, чем та, которая использует цикл.

def cross(series, cross=0, direction='cross'): 
    """ 
    Given a Series returns all the index values where the data values equal 
    the 'cross' value. 

    Direction can be 'rising' (for rising edge), 'falling' (for only falling 
    edge), or 'cross' for both edges 
    """ 
    # Find if values are above or bellow yvalue crossing: 
    above=series.values > cross 
    below=np.logical_not(above) 
    left_shifted_above = above[1:] 
    left_shifted_below = below[1:] 
    x_crossings = [] 
    # Find indexes on left side of crossing point 
    if direction == 'rising': 
     idxs = (left_shifted_above & below[0:-1]).nonzero()[0] 
    elif direction == 'falling': 
     idxs = (left_shifted_below & above[0:-1]).nonzero()[0] 
    else: 
     rising = left_shifted_above & below[0:-1] 
     falling = left_shifted_below & above[0:-1] 
     idxs = (rising | falling).nonzero()[0] 

    # Calculate x crossings with interpolation using formula for a line: 
    x1 = series.index.values[idxs] 
    x2 = series.index.values[idxs+1] 
    y1 = series.values[idxs] 
    y2 = series.values[idxs+1] 
    x_crossings = (cross-y1)*(x2-x1)/(y2-y1) + x1 

    return x_crossings 

# Test it out: 
time = [0, 0.1, 0.21, 0.31, 0.40, 0.49, 0.51, 0.6, 0.71, 0.82, 0.93] 
voltage = [1, -1, 1.1, -0.9, 1, -1, 0.9,-1.2, 0.95, -1.1, 1.11] 
df = DataFrame(data=voltage, index=time, columns=['voltage']) 
x_crossings = cross(df['voltage']) 
y_crossings = np.zeros(x_crossings.shape) 
plt.plot(time, voltage, '-ob', x_crossings, y_crossings, 'or') 
plt.grid(True) 

Это было вполне удовлетворительно, когда это сработало. Любые улучшения, которые могут быть сделаны?

+0

Прохладный. Я создал проблему об этом здесь: https://github.com/pydata/pandas/issues/1256. Мне нужно будет ближе рассмотреть его когда-нибудь в будущем –

+0

Любые другие методы интерполяции, которые могут быть использованы для большей точности в получении точек пересечения? – Chetan

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