Более быстрый способ выбора строк и применения функции

Я хочу рассчитать расстояние между текущей и следующей конечной точкой в ​​данных ниже. Один ключ может иметь несколько конечных точек (x, y).

Я ищу более быстрое решение вышеупомянутой проблемы, чем то, что у меня есть, поскольку на его выполнение уходит много часов.

проверьте формулу для расчета расстояния между двумя конечными точками

Данные


Key  x     y

1   87.4375 28.921875
1   97.4375 23.921875
1   97.4375 23.921875
1   97.4375 23.921875
1   97.4375 23.921875
2   86.4375 24.921875
2   85.4375 24.921875
2   93.4375 29.921875
2   86.4375 24.921875
2   85.4375 24.921875
2   93.4375 29.921875

Что я пробовал:

Решение 1

new_df= pd.DataFrame(columns=['key', 'distance'])
for key in orig_df.key.values:
    
    x2 = orig_df.loc[orig_df.key== key ,'x'].shift(-1)
    y2 = orig_df.loc[orig_df.key== key ,'y'].shift(-1)
    x1 = orig_df.loc[orig_df.key== key ,'x']
    y1 = orig_df.loc[orig_df.key== key ,'y']
    distance = cal_distance(x2,y2,x1,y1)
    new_df.append({'key': key, 'distance': distance}, ignore_index=True)

def cal_distance(x2,y2,x1,y1):
    return abs(np.sqrt( np.square(x2 - x1) + np.square(y2 - y1))).sum()

Здесь много строк, и выполнение приведенного выше кода занимает несколько часов.

Решение 2 (не сработало)

orig_df.groupby('key').pipe(cal_distance(orig_df.x.shift(-1),orig_df.y.shift(-1),orig_df.x,orig_df.y))

Я пробовал сгруппировать по ключу.

Любая помощь очень ценится.

Ожидаемый результат

    key distance
0   1   11.18034
1   2   29.470288

person Shreesha Kumar Bhat    schedule 10.12.2019    source источник
comment
Пожалуйста, не включайте информацию в изображения, напишите ее в самом посте. Мы также были бы признательны, если бы вы могли поделиться этими данными в более простом для использования формате, например CSV, или даже просто в выводе to_dict(). А пока несколько общих советов: не используйте .values. Не используйте явные циклы. Не добавляйте в DataFrame. Неудивительно, что на это уходит часы.   -  person AMC    schedule 10.12.2019
comment
Каков ожидаемый результат?   -  person Alexander    schedule 10.12.2019
comment
Что за ключ? Какая конечная точка?   -  person AMC    schedule 10.12.2019
comment
Для Key =2 есть три балла. Для какой из этих 3 точек вы хотите рассчитать расстояние?   -  person shaik moeed    schedule 10.12.2019
comment
@Alexander Был ли ваш ключ первым столбцом в фрейме данных. в ответ на меня?   -  person AMC    schedule 10.12.2019
comment
ОП, я даже не могу заставить ваш код работать, поэтому в настоящее время у нас нет возможности понять, что именно вы хотите. Кстати, откуда вы получаете свой вклад?   -  person AMC    schedule 10.12.2019
comment
@ Александр Это был не мой вопрос.   -  person AMC    schedule 10.12.2019
comment
@Alexander Wait - это Шесть пар точек для key = 2 тоже ответ на мой комментарий?   -  person AMC    schedule 10.12.2019
comment
Как задать вопрос Разместите вопрос и ответьте на отзыв После публикации оставьте вопрос открытым в браузере ненадолго , и посмотрите, есть ли у кого-нибудь комментарии. Если вы пропустили очевидную информацию, будьте готовы ответить, отредактировав свой вопрос, чтобы включить его. Если кто-то отправит ответ, будьте готовы опробовать его и оставить отзыв!   -  person Alexander    schedule 10.12.2019
comment
@ AlexanderCécile Спасибо за обратную связь об использовании явных циклов и использовании переполнения стека.   -  person Shreesha Kumar Bhat    schedule 11.12.2019
comment
@Alexander Я уже добавил вывод. У меня плохо   -  person Shreesha Kumar Bhat    schedule 11.12.2019
comment
@ShreeshaKumarBhat Можете уточнить, что именно вы пытаетесь сделать?   -  person AMC    schedule 11.12.2019
comment
@ShreeshaKumarBhat Почему ты принял мой ответ? «Ожидаемый результат» выше уже неверен?   -  person AMC    schedule 11.12.2019
comment
Я согласился, потому что это помогло решить вопрос. Теперь время выполнения сократилось до 9 минут.   -  person Shreesha Kumar Bhat    schedule 11.12.2019
comment
@ShreeshaKumarBhat Хорошо, вам все еще нужны эти два выходных значения. Я немного отредактирую свой пост.   -  person AMC    schedule 11.12.2019
comment
@ShreeshaKumarBhat Можете объяснить, что вы пытаетесь сделать? Вы никогда этого не делали, описание проблемы мне до сих пор неоднозначно и непонятно.   -  person AMC    schedule 12.12.2019


Ответы (2)


РЕДАКТИРОВАТЬ: Как и ожидалось, фактическое желаемое поведение немного отличалось от того, что я предполагал. Я буду обновлять свой ответ, чтобы отразить это, это должна быть небольшая настройка.

С чего начать. Ваш код чрезвычайно унидиоматичен. Я совершенно уверен, что идиоматический код Pandas не займет часы, чтобы получить результат. Непонятно даже, зачем вы вообще используете Pandas. Как вы увидите в моем решении ниже, использование Pandas делает вещи намного более неудобными и неуклюжими, чем они должны быть.

Конечно, это может быть не желаемый результат, так как вопрос неоднозначный и непонятный.

from io import StringIO

import numpy as np
import pandas as pd

raw_str = \
    '''
    key  x     y

    1   87.4375 28.921875
    1   97.4375 23.921875
    1   97.4375 23.921875
    1   97.4375 23.921875
    1   97.4375 23.921875
    2   86.4375 24.921875
    2   85.4375 24.921875
    2   93.4375 29.921875
    2   86.4375 24.921875
    2   85.4375 24.921875
    2   93.4375 29.921875
    '''

df = pd.read_csv(StringIO(raw_str), delim_whitespace=True)

rows_list = []

for k, v in df.groupby('key'):
    coords = v[['x', 'y']].to_numpy()
    coords_shifted = np.roll(coords, shift=-1, axis=0)
    dist = np.linalg.norm(coords[:-1] - coords_shifted[:-1], axis=1).sum()
    rows_list.append((k, dist))

res_df = pd.DataFrame(data=rows_list, columns=['key', 'distance'])

print(res_df)

res_df:

   key   distance
0    1  11.180340
1    2  29.470288
person AMC    schedule 10.12.2019
comment
Но это решение дает неверный результат. res_df.groupby('key').sum() не дает ожидаемого результата. - person Alexander; 11.12.2019
comment
@Alexander Я тоже в замешательстве. Хотя я буду обновлять свое решение, я бы хотел, чтобы OP рассказала нам, как они в итоге это использовали. - person AMC; 12.12.2019
comment
@Alexander О, а что касается точной причины, по которой вывод отличается, это потому, что я использовал numpy.roll() вместо shift(). - person AMC; 12.12.2019
comment
Я изменил строку кода dists = np.linalg.norm(coords[:-1] - coords_shifted[:-1], axis=1), чтобы не вычислять скользящее расстояние между последней и первой конечными точками для каждой клавиши. - person Shreesha Kumar Bhat; 12.12.2019
comment
@ShreeshaKumarBhat Можете объяснить, что вы делаете и для чего это? - person AMC; 12.12.2019
comment
@ AlexanderCécile Например: 4 1 11.180340 в res def - это расстояние между точками последней координаты key1 (97.4375,23.921875) и первой координатой key1 (87.4375,28.921875). Я не хочу включать это расстояние - person Shreesha Kumar Bhat; 12.12.2019
comment
@ShreeshaKumarBhat Я имел в виду, для чего нужна эта программа ?! Что он делает. Вы говорите о ключах и точках, не объясняя, что они собой представляют. - person AMC; 12.12.2019
comment
@ShreeshaKumarBhat Я редактировал программу, результат теперь такой же, как и желаемый. Я все еще был бы признателен за некоторую ясность в вопросах, которые я задал, включая сбивающий с толку выбор Pandas вместо NumPy, а также то, как вы в первую очередь получаете данные. - person AMC; 13.12.2019
comment
@Alexander Обновлено, теперь он должен давать правильный результат! - person AMC; 13.12.2019
comment
@Alexander Возвращение к этому посту заставило меня понять, что я должен избавиться от этого цикла и создать функцию для использования с _1 _... - person AMC; 13.12.2019
comment
С некоторыми небольшими изменениями мое чистое решение Pandas работает почти на 30% быстрее, чем ваше решение Numpy на 1 м строках образцов данных. - person Alexander; 13.12.2019
comment
@Alexander Это из последних внесенных вами изменений? - person AMC; 13.12.2019
comment
@Alexander Хорошо, молодец! Я посмотрю и откорректирую свое решение завтра. - person AMC; 13.12.2019

# Random data (1m rows, 1000 keys)

np.random.seed(0)

rows = 1_000_000
keys = 1000
keys = np.random.randint(low=1, high=keys, size=rows)
x = np.random.rand(rows) * 360
y = np.random.rand(rows) * 360
df = pd.DataFrame({'key': keys, 'x': x, 'y': y})

Решение

Это вычисляет расстояние между последующими точками в каждой ключевой группе, а затем суммирует общее расстояние для каждой клавиши. Основываясь на образце данных с 1 миллионом строк выше, это занимает около 0,8 секунды.

df.sort_values('key', kind='mergesort', inplace=True)  # `mergesort` is the only stable algorithm.
distances = (
    df
    .apply(lambda s: s.diff().pow(2)).sum(axis=1).pow(0.5)  # Calculate distances b/w endpoints.
    .groupby(df['key'])
    .apply(lambda s: s.iloc[1:].sum())  # Sum the distances between endpoints by key.
)

>>> distances.head()
key
1    198431.901333
2    192694.829351
3    176125.208567
4    186942.057497
5    182029.077809
dtype: float64

ОЖИДАЕМЫЙ ВЫВОД Используя исходный фрейм данных OP, приведенное выше решение дает ожидаемый результат:

>>> distances
key
1    11.180340
2    29.470288
dtype: float64

Комментарии к вашему коду

Глядя на свой код в решении 1, вы перебираете _все_ значения ключей (for key in orig_df.key.values:). В приведенном выше примере данных это будет 11 циклов. Затем для каждого цикла вы используете .loc, чтобы найти значения для этого ключа. Обратите внимание, что каждая .loc операция возвращает одну и ту же группу на основе ключа, а не строки.

Предполагая, что key=2, ваши данные выглядят так:

         x1       x2         y1         y2
5   86.4375  85.4375  24.921875  24.921875
6   85.4375  93.4375  24.921875  29.921875
7   93.4375  86.4375  29.921875  24.921875
8   86.4375  85.4375  24.921875  24.921875
9   85.4375  93.4375  24.921875  29.921875
10  93.4375      NaN  29.921875        NaN

Итак, результат np.sqrt( np.square(x1 - x2) + np.square(y1 - y2)) таков:

5     1.000000
6     9.433981
7     8.602325
8     1.000000
9     9.433981
10         NaN
dtype: float64

Где вы затем неправильно суммируете столбец. Вам нужно суммировать каждую строку, определяющую ось (.sum(axis=1). Кроме того, я не уверен, что ваш сдвиг в правильном направлении, но это зависит от того, как структурированы ваши исходные данные. В конце концов, это, вероятно, не имеет значения, но это зависит от того, как вы собираетесь использовать результаты.

В качестве второстепенного момента нет необходимости брать абсолютное значение в вашей функции, так как сумма двух возведенных в квадрат значений всегда будет неотрицательной, и, следовательно, квадратный корень суммы будет таким же.

person Alexander    schedule 10.12.2019
comment
Согласно образцам данных, предоставленным OP, на выходе должны быть только два значения (уникальные ключи). - person shaik moeed; 10.12.2019
comment
@shaikmoeed Откуда ты это знаешь? - person AMC; 10.12.2019
comment
@shaikmoeed Опять же, я не думаю, что есть какое-либо указание на это. - person AMC; 10.12.2019
comment
В вопросе OP есть некоторая двусмысленность. Из OP я хочу рассчитать расстояние между текущей и следующей конечной точкой в ​​данных ниже. Один ключ может иметь несколько конечных точек (x, y). и groupby код в OP заставляет меня выбирать 1-е и 2-е значения каждой группы. - person shaik moeed; 10.12.2019
comment
@Alexander OP ни одно решение не работает. И я твердо верю, что для каждой группы есть повторяющиеся точки данных, поэтому OP сказал текущая и следующая конечная точка - person shaik moeed; 10.12.2019
comment
@Alexander Хорошее решение, но его выполнение по-прежнему занимает несколько часов. Векторизация с решением numpy в принятом ответе работает хорошо. Время исполнения сокращено до 9 минут. - person Shreesha Kumar Bhat; 11.12.2019
comment
@ShreeshaKumarBhat Насколько велики ваши данные? Я думаю, что это решение будет хорошим с точки зрения производительности. - person AMC; 11.12.2019
comment
@ AlexanderCécile 2,4 ГБ после уменьшения объема памяти исходного набора данных. Я выполняю на ядре kaggle - person Shreesha Kumar Bhat; 11.12.2019
comment
@ShreeshaKumarBhat Вы уверены, что выполняете правильную операцию? Вы можете поделиться этими данными? - person AMC; 12.12.2019