У меня есть фрейм данных (~ 30 000 строк) количество поездок по коду станции.
|station from|station to|count|
|:-----------|:---------|:----|
|20001 |20040 |55 |
|20040 |20001 |67 |
|20007 |20080 |100 |
|20080 |20007 |50 |
как можно получить df где есть количество обратных рейсов и убраны лишние строки обратных рейсов, типа
|station from|station to|count|count_back|
|:-----------|:---------|:----|:---------|
|20001 |20040 |55 |67 |
|20007 |20080 |100 |50 |
мое решение
Но это кажется очень неэффективным
Давайте попробуем sort
станции и пивот:
# the two stations
cols = ['station from', 'station to']
# back and fort
df['col'] = np.where(df['station from'] < df['station to'], 'count', 'count_back')
# rearrange the stations
df[cols] = np.sort(df[cols], axis=1)
# pivot
print(df.pivot(index=cols, columns='col', values='count')
.reset_index()
)
Выход:
col station from station to count count_back
0 20001 20040 55 67
1 20007 20080 100 50
но немного медленнее и менее надежно ;-)
Вот простое решение, которое обрабатывает случаи без кругового обхода.
import pandas as pd
import numpy as np
df = pd.DataFrame({"station from":[20001,20040,20007,20080, 2, 3],
"station to":[20040,20001,20080,20007, 1, 4],
"count":[55,67,100,50, 20, 40]})
df
df = df.set_index(["station from", "station to"])
df["count_back"] = df.apply(lambda row: df["count"].get((row.name[::-1])), axis=1)
mask_rows_to_delete = df.apply(lambda row: row.name[0] > row.name[1] and row.name[::-1] in df.index, axis=1)
df = df[~mask_rows_to_delete].reset_index()
df
Спасибо за ваш ответ. В моем датафрейме есть поездки без обратного пути, поэтому я получаю эту ошибку Passing list-likes to .loc or [] with any missing labels is no longer supported
Я опубликовал новое решение (намного проще), которое обрабатывает этот случай.
Хорошо, отлично, так что, может быть, вы сможете принять мой ответ или хотя бы проголосовать за него :-)
Это работает даже при дублировании записей и довольно быстро (<250 мс на миллион строк):
def roundtrip(df):
a, b, c, d = 'station from', 'station to', 'count', 'count_back'
idx = df[a] > df[b]
df = df.assign(**{d: 0})
df.loc[idx, [a, b, c, d]] = df.loc[idx, [b, a, d, c]].values
return df.groupby([a, b]).sum()
На вашем примере данных (и да, вы можете .reset_index()
, если хотите):
>>> roundtrip(df)
count count_back
station from station to
20001 20040 54 55
20007 20080 100 50
Тест времени:
n = 1_000_000
df = pd.DataFrame({
'station from': np.random.randint(1000, 2000, n),
'station to': np.random.randint(1000, 2000, n),
'count': np.random.randint(0, 200, n),
})
%timeit roundtrip(df)
217 ms ± 2.22 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
(На 100 тыс. строк это 32,4 мс ± 333 мкс на цикл)
Это похоже на волшебство. Спасибо! Могу ли я использовать аналогичный для дополнительного строкового столбца (идентификатор lgot), как этот |station from|station to|lgot tipe|count|
, или проще преобразовать тип lgot в идентификатор lgot и объединить его со станциями, а затем использовать предложенную функцию
возможно, но трудно догадаться, не видя примера... Кроме того, не забудьте выбрать принятый ответ и проголосовать за все полезные ответы.
Это сработало для меня, полученные данные такие же, как решение Пьера Д.