データフレームc
にはさまざまな列があります。また、arr
は、c
のサブセットに対応するデータフレームです:_arr = c[c['A_D'] == 'A']
_。
私のコードの主なアイデアは、c
- dataframe内のすべての行を反復処理し、特定の条件が発生する可能性のあるすべてのケース(arr
dataframe内)を検索することです。
c['A_D'] == D
_および_c['Already_linked'] == 0
_であった行のみを反復処理する必要がありますhour
データフレームのarr
は、c
データフレームの_hour_aux
_より小さくなければなりませんarr
データフレームの_Already_linked
_列はゼロでなければなりません:_arr.Already_linked == 0
_Terminal
とOperator
は、cおよびarr
データフレームで同じである必要があります現在、条件はブールインデックスとgroupby get_groupの両方を使用して保存されます。
arr
データフレームをグループ化します:_g = groups.get_group((row.Operator, row.Terminal
_))c
データフレームの時間よりも小さく、Already_linked == 0である到着のみを選択します:vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
すべての条件を検証するc
データフレーム内の行ごとに、vb
データフレームが作成されます。当然、このデータフレームは、反復ごとに長さが異なります。 vb
データフレームを作成した後、私の目標は_vb.START
_とc [vb
]の間の時間を最小にするx
データフレームのインデックスを選択することです。このインデックスに対応するFightID
は、列c
のa
データフレームに保存されます。さらに、到着は出発にリンクされていたため、arr
データフレームの列_Already_linked
_は0から1に変更されます。
arr
データフレームの列_Already_linked
_は、繰り返しごとに変化する可能性があることに注意することが重要です(_arr.Already_linked == 0
_はvb
データフレームを作成する条件の1つです) 。したがって、このコードを並列化することはできません。
効率のために既にc.itertuples()
を使用しましたが、c
には数百万の行があるため、このコードは依然として時間がかかりすぎます。
他のオプションは、すべての行に_pd.apply
_を使用することです。それにもかかわらず、各ループでc
とarr
の両方で値が変化するため、これは本当に簡単ではありません(また、_pd.apply
_でも非常に遅いと思います) 。
ベクトル化されたソリューションでこれをforループに変換する方法はありますか(または実行時間を10倍(可能であればさらに)短縮してください)?
初期データフレーム:
_START END A_D Operator FlightID Terminal TROUND_ID tot
0 2017-03-26 16:55:00 2017-10-28 16:55:00 A QR QR001 4 QR002 70
1 2017-03-26 09:30:00 2017-06-11 09:30:00 D DL DL001 3 " " 84
2 2017-03-27 09:30:00 2017-10-28 09:30:00 D DL DL001 3 " " 78
3 2017-10-08 15:15:00 2017-10-22 15:15:00 D VS VS001 3 " " 45
4 2017-03-26 06:50:00 2017-06-11 06:50:00 A DL DL401 3 " " 9
5 2017-03-27 06:50:00 2017-10-28 06:50:00 A DL DL401 3 " " 19
6 2017-03-29 06:50:00 2017-04-19 06:50:00 A DL DL401 3 " " 3
7 2017-05-03 06:50:00 2017-10-25 06:50:00 A DL DL401 3 " " 32
8 2017-06-25 06:50:00 2017-10-22 06:50:00 A DL DL401 3 " " 95
9 2017-03-26 07:45:00 2017-10-28 07:45:00 A DL DL402 3 " " 58
_
必要な出力(以下のデータフレームでは一部の列が除外されています。a
列と_Already_linked
_列のみが関連しています):
_ START END A_D Operator a Already_linked
0 2017-03-26 16:55:00 2017-10-28 16:55:00 A QR 0 1
1 2017-03-26 09:30:00 2017-06-11 09:30:00 D DL DL402 1
2 2017-03-27 09:30:00 2017-10-28 09:30:00 D DL DL401 1
3 2017-10-08 15:15:00 2017-10-22 15:15:00 D VS No_link_found 0
4 2017-03-26 06:50:00 2017-06-11 06:50:00 A DL 0 0
5 2017-03-27 06:50:00 2017-10-28 06:50:00 A DL 0 1
6 2017-03-29 06:50:00 2017-04-19 06:50:00 A DL 0 0
7 2017-05-03 06:50:00 2017-10-25 06:50:00 A DL 0 0
8 2017-06-25 06:50:00 2017-10-22 06:50:00 A DL 0 0
9 2017-03-26 07:45:00 2017-10-28 07:45:00 A DL 0 1
_
コード:
_groups = arr.groupby(['Operator', 'Terminal'])
for row in c[(c.A_D == "D") & (c.Already_linked == 0)].itertuples():
try:
g = groups.get_group((row.Operator, row.Terminal))
vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
aux = (vb.START - row.x).abs().idxmin()
c.loc[row.Index, 'a'] = vb.loc[aux].FlightID
arr.loc[aux, 'Already_linked'] = 1
continue
except:
continue
c['Already_linked'] = np.where((c.a != 0) & (c.a != 'No_link_found') & (c.A_D == 'D'), 1, c['Already_linked'])
c.Already_linked.loc[arr.Already_linked.index] = arr.Already_linked
c['a'] = np.where((c.Already_linked == 0) & (c.A_D == 'D'),'No_link_found',c['a'])
_
初期のc
データフレームのコード:
_import numpy as np
import pandas as pd
import io
s = '''
A_D Operator FlightID Terminal TROUND_ID tot
A QR QR001 4 QR002 70
D DL DL001 3 " " 84
D DL DL001 3 " " 78
D VS VS001 3 " " 45
A DL DL401 3 " " 9
A DL DL401 3 " " 19
A DL DL401 3 " " 3
A DL DL401 3 " " 32
A DL DL401 3 " " 95
A DL DL402 3 " " 58
'''
data_aux = pd.read_table(io.StringIO(s), delim_whitespace=True)
data_aux.Terminal = data_aux.Terminal.astype(str)
data_aux.tot= data_aux.tot.astype(str)
d = {'START': ['2017-03-26 16:55:00', '2017-03-26 09:30:00','2017-03-27 09:30:00','2017-10-08 15:15:00',
'2017-03-26 06:50:00','2017-03-27 06:50:00','2017-03-29 06:50:00','2017-05-03 06:50:00',
'2017-06-25 06:50:00','2017-03-26 07:45:00'], 'END': ['2017-10-28 16:55:00' ,'2017-06-11 09:30:00' ,
'2017-10-28 09:30:00' ,'2017-10-22 15:15:00','2017-06-11 06:50:00' ,'2017-10-28 06:50:00',
'2017-04-19 06:50:00' ,'2017-10-25 06:50:00','2017-10-22 06:50:00' ,'2017-10-28 07:45:00']}
aux_df = pd.DataFrame(data=d)
aux_df.START = pd.to_datetime(aux_df.START)
aux_df.END = pd.to_datetime(aux_df.END)
c = pd.concat([aux_df, data_aux], axis = 1)
c['A_D'] = c['A_D'].astype(str)
c['Operator'] = c['Operator'].astype(str)
c['Terminal'] = c['Terminal'].astype(str)
c['hour'] = pd.to_datetime(c['START'], format='%H:%M').dt.time
c['hour_aux'] = pd.to_datetime(c['START'] - pd.Timedelta(15, unit='m'),
format='%H:%M').dt.time
c['start_day'] = c['START'].astype(str).str[0:10]
c['end_day'] = c['END'].astype(str).str[0:10]
c['x'] = c.START - pd.to_timedelta(c.tot.astype(int), unit='m')
c["a"] = 0
c["Already_linked"] = np.where(c.TROUND_ID != " ", 1 ,0)
arr = c[c['A_D'] == 'A']
_
このソリューションは、 numpy.in1dを使用するpd.DataFrame.isinを使用します
明らかに、「isin」は小さなデータセット(このサンプルのような)では必ずしも高速ではありませんが、大きなデータセットではかなり高速です。パフォーマンスを判断するには、データに対して実行する必要があります。
c = pd.concat([c] * 10000, ignore_index=True)
を使用してデータセットを展開しました
新しい方法:isinとapplyを使用する
def apply_do_g(it_row):
"""
This is your function, but using isin and apply
"""
keep = {'Operator': [it_row.Operator], 'Terminal': [it_row.Terminal]} # dict for isin combined mask
holder1 = arr[list(keep)].isin(keep).all(axis=1) # create boolean mask
holder2 = arr.Already_linked.isin([0]) # create boolean mask
holder3 = arr.hour < it_row.hour_aux # create boolean mask
holder = holder1 & holder2 & holder3 # combine the masks
holder = arr.loc[holder]
if not holder.empty:
aux = np.absolute(holder.START - it_row.x).idxmin()
c.loc[it_row.name, 'a'] = holder.loc[aux].FlightID # use with apply 'it_row.name'
arr.loc[aux, 'Already_linked'] = 1
def new_way_2():
keep = {'A_D': ['D'], 'Already_linked': [0]}
df_test = c[c[list(keep)].isin(keep).all(axis=1)].copy() # returns the resultant df
df_test.apply(lambda row: apply_do_g(row), axis=1) # g is multiple DataFrames"
#call the function
new_way_2()
あなたの質問は、forループをベクトル化する方法があるかどうかでしたが、その質問はあなたの本当に欲しいものを隠していると思います。これはコードを高速化する簡単な方法です。パフォーマンスに関する質問については、常にプロファイリングから始めるのが適切です。ただし、コードの主要な操作は.query(row.query_string)
であるという強い疑いがあります。 arr
が大きい場合、すべての行で実行するとコストがかかります。
任意のクエリの場合、反復間の依存関係を削除し、高価なステップを並列化しない限り、そのランタイムを実際に改善することはできません。あなたは少し幸運かもしれませんが。クエリ文字列は、常に2つの異なる列をチェックして、それらが関心のあるものと等しいかどうかを確認します。ただし、arr
のスライス全体を処理する必要がある各行に対して。スライスは毎回変更されるため、問題が発生する可能性がありますが、いくつかのアイデアがあります。
arr
をスライスしているので、小さなオブジェクトを繰り返し処理するために_arr.Already_Linked==0
_行だけのビューを維持してください。arr
をTerminal
とOperator
でグループ化する必要があります。次に、arr
のすべてを実行する代わりに、最初に目的のグループを選択してから、スライスとフィルタリングを実行します。これには、_query_string
_の正確な実装を少し再考する必要がありますが、多くの端末とオペレーターがいる場合、通常はarr
よりもはるかに小さなオブジェクトで作業するという利点があります。さらに、groupbyによって暗黙的に行われたため、そのオブジェクトを照会する必要さえありません。aux.hour
_が通常_row.hour_aux
_とどのように関係するかに応じて、aux
に関して先頭でhour
をソートすることで改善される場合があります。不等式演算子を使用するだけでは、おそらくゲインは表示されませんが、カットオフポイントの対数検索と組み合わせて、そのカットオフポイントまでスライスすることができます。arr
で実行しているクエリを再構築する方法は、すべての行が単によりもかなり多くの利益をもたらすと思いますフレームワークの切り替えまたはビットとピースのベクトル化。これらのポイントのいくつかを少し拡張し、@ DJKのコードを少し調整して、次の変更があったときに何が起こるか見てみましょう。
_groups = arr.groupby(['Operator', 'Terminal'])
for row in c[(c.A_D == 'D') & (c.Already_linked == 0)].itertuples():
g = groups.get_group((row.Operator, row.Terminal))
vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
try:
aux = (vb.START - row.x).abs().idxmin()
print(row.x)
c.loc[row.Index, 'a'] = vb.loc[aux,'FlightID']
g.loc[aux, 'Already_linked'] = 1
continue
except:
continue
_
クエリが非常に遅い理由の一部は、毎回arr
をすべて検索しているためです。対照的に、.groupby()
は1つのクエリとほぼ同時に実行されますが、以降の反復ごとにを使用すると、.get_group()
を使用して効率的に検索できます気になるデータの小さなサブセット。
ベンチマークの際に役立つ(非常に粗雑な)経験則は、10億ものものに1秒かかるということです。数百万の行など、数百万ものもので測定されたものよりもはるかに長い時間を見ている場合、それらの行のそれぞれに対して、何十億もの操作を行うために大量のことをしていることを意味します。これにより、操作の数を減らすためのより良いアルゴリズムの可能性がたくさん残されますが、ベクトル化は実際には一定のファクターの改善のみをもたらします(そして多くの文字列/クエリ操作ではそれほど改善されません)。
これはベクトル化されたソリューションではありませんが、サンプルデータセットが実際のデータセットを模倣している場合は、物事を迅速にスピードアップする必要があります。現在、すべての行でループする時間を無駄にしていますが、['A_D'] == 'D'
および['Already_linked'] ==0
。代わりにifを削除し、初期データフレームの30%のみである切り捨てられたデータフレームをループします
for row in c[(c.A_D == 'D') & (c.Already_linked == 0)].itertuples():
vb = arr[(arr.Already_linked == 0) & (arr.hour < row.hour_aux)].copy().query(row.query_string)
try:
aux = (vb.START - row.x).abs().idxmin()
print(row.x)
c.loc[row.Index, 'a'] = vb.loc[aux,'FlightID']
arr.loc[aux, 'Already_linked'] = 1
continue
except:
continue
問題は、データベース操作で最も一般的な問題の1つのように見えます。あなたがタスクを定式化していないので、私はあなたが何を得たいかを完全には理解していません。考えられる解決策については、-ループをまったく避けてください。
time, FlightID, Operator, Terminal, A_D
列の非常に長いtable
があります。私があなたを正しく理解していれば、他の列と日付は関係ありません。また、start_time
とend_time
はすべての行で同じです。ところで、コードtable.loc[:, 'time'] = table.loc[:, 'START'].dt.time
でtime
列を取得できます。
table = table.drop_duplicates(subset=['time', 'FlightID', 'Operator', 'Terminal'])
。そして、table
が大幅に短くなります。
table
をtable_arr
の値に従ってtable_dep
とA_D
に分割します:table_arr = table.loc[table.loc[:, 'A_D'] == 'A', ['FlightID', 'Operator', 'Terminal', 'time']]
、table_dep = table.loc[table.loc[:, 'A_D'] == 'D', ['FlightID', 'Operator', 'Terminal', 'time']]
1行で取得できるループで取得しようとしたすべてのように思えます:table_result = table_arr.merge(table_dep, how='right', on=['Operator', 'Terminal'], suffixes=('_arr', '_dep'))
。基本的に、SQLのJOIN
と同じ操作です。
あなたの問題の私の理解とあなたが提供した小さなデータを持っていると、非常に高速にループすることなく、目的の出力(すべてのFlightID_dep
値のFlightID_arr
とFlightID_dep
の対応)を得ることができます。 table_result
は:
FlightID_arr Operator Terminal time_arr FlightID_dep time_dep
0 DL401 DL 3 06:50:00 DL001 09:30:00
1 DL402 DL 3 07:45:00 DL001 09:30:00
2 NaN VS 3 NaN VS001 15:15:00
もちろん、一般的な場合(実際のデータを使用する場合)、もう1つのステップが必要になります-条件table_result
またはその他の条件でtime_arr < time_dep
をフィルタリングします。残念ながら、提供されたデータでは問題を完全に解決するには不十分です。
完全なコードは次のとおりです。
import io
import pandas as pd
data = '''
START,END,A_D,Operator,FlightID,Terminal,TROUND_ID,tot
2017-03-26 16:55:00,2017-10-28 16:55:00,A,QR,QR001,4,QR002,70
2017-03-26 09:30:00,2017-06-11 09:30:00,D,DL,DL001,3,,84
2017-03-27 09:30:00,2017-10-28 09:30:00,D,DL,DL001,3,,78
2017-10-08 15:15:00,2017-10-22 15:15:00,D,VS,VS001,3,,45
2017-03-26 06:50:00,2017-06-11 06:50:00,A,DL,DL401,3,,9
2017-03-27 06:50:00,2017-10-28 06:50:00,A,DL,DL401,3,,19
2017-03-29 06:50:00,2017-04-19 06:50:00,A,DL,DL401,3,,3
2017-05-03 06:50:00,2017-10-25 06:50:00,A,DL,DL401,3,,32
2017-06-25 06:50:00,2017-10-22 06:50:00,A,DL,DL401,3,,95
2017-03-26 07:45:00,2017-10-28 07:45:00,A,DL,DL402,3,,58
'''
table = pd.read_csv(io.StringIO(data), parse_dates=[0, 1])
table.loc[:, 'time'] = table.loc[:, 'START'].dt.time
table = table.drop_duplicates(subset=['time', 'FlightID', 'Operator', 'Terminal'])
table_arr = table.loc[table.loc[:, 'A_D'] == 'A', ['FlightID', 'Operator', 'Terminal', 'time']]
table_dep = table.loc[table.loc[:, 'A_D'] == 'D', ['FlightID', 'Operator', 'Terminal', 'time']]
table_result = table_arr.merge(
table_dep,
how='right',
on=['Operator', 'Terminal'],
suffixes=('_arr', '_dep'))
print(table_result)