我正在解决一个试图在车站找到火车会议的问题,但我很难找到一种方法在不使用嵌套 for 循环的情况下进行必要的比较(这是减慢速度的方法,我有数十万个数据点(。
我的数据帧行包含以下有用数据:到达时间(日期时间(、出发时间(日期时间(、唯一的列车 ID(字符串(、列车在开始时间和终点时间之间的车站(字符串(以及它遇到的列车的列车 ID 的单元格(字符串,开始时为空(。我想找到所有相遇的行对,这意味着我希望满足:
从第 1 行到达到离开的- 时间间隔与第 2 行到达到离开的时间间隔重叠。
- 位于同一车站。
此外,没有涉及两列以上火车的会议。
我尝试了以下方法(下面的代码(:我在到达和离开时间之外创建了间隔对象。然后我使用 nested for 循环将每一行间隔与其他每行进行比较,如果它们重叠,我检查电台是否匹配。如果他们这样做了,我将每个火车ID存储在另一个火车会议单元中。
df_dsp['interval'] = [func(x,y) for x, y in zip(df_dsp['arrival'], df_dsp['departure'])]
meetings = np.empty([])
for i in range (1,len(df.index)):
for q in range (1,len(df.index)):
if (i < q): # Train meetings are symmetric.
if df.iloc[i, df.columns.get_loc('interval')].overlaps(df.iloc[q, df.columns.get_loc('interval')]):
if df.iloc[i, df.columns.get_loc('station')] == df.iloc[q, df.columns.get_loc('station')]:
df.iloc[i, df.columns.get_loc('train_id_meeting')] = df.iloc[q, df.columns.get_loc('train_id')]
df.iloc[q, df.columns.get_loc('train_id_meeting')] = df.iloc[i, df.columns.get_loc('train_id')]
我已经看过类似的问题,但很难有效地将它们应用于我的数据集。我的问题是:如何更快地执行这些比较?
编辑: 我无法给出数据库(有些机密(,但我制作了一个具有代表性的数据集。
d = {'arrival': [pd.Timestamp(datetime.datetime(2012, 5, 1, 1)), pd.Timestamp(datetime.datetime(2012, 5, 1, 3)),
pd.Timestamp(datetime.datetime(2012, 5, 1, 6)), pd.Timestamp(datetime.datetime(2012, 5, 1, 4))],
'departure': [pd.Timestamp(datetime.datetime(2012, 5, 1, 3)), pd.Timestamp(datetime.datetime(2012, 5, 1, 5)),
pd.Timestamp(datetime.datetime(2012, 5, 1, 7)), pd.Timestamp(datetime.datetime(2012, 5, 1, 6))],
'station': ["a", "b", "a", "b"],
'train_id': [1, 2, 3, 4],
'meetings': [np.nan, np.nan, np.nan, np.nan]}
df = pd.DataFrame(data=d)
在此样本数据中,第 2 行和第 4 行表示在车站"b"相遇的列车。如果可以在不使用 Interval-对象的情况下更快地完成此操作,我很乐意使用它。
初始化数据帧。
d = {'arrival': [pd.Timestamp(datetime.datetime(2012, 5, 1, 1)), pd.Timestamp(datetime.datetime(2012, 5, 1, 3)),
pd.Timestamp(datetime.datetime(2012, 5, 1, 6)), pd.Timestamp(datetime.datetime(2012, 5, 1, 4))],
'departure': [pd.Timestamp(datetime.datetime(2012, 5, 1, 3)), pd.Timestamp(datetime.datetime(2012, 5, 1, 5)),
pd.Timestamp(datetime.datetime(2012, 5, 1, 7)), pd.Timestamp(datetime.datetime(2012, 5, 1, 6))],
'station': ["a", "b", "a", "b"],
'train_id': [1, 2, 3, 4],
'meetings': [np.nan, np.nan, np.nan, np.nan]}
df = pd.DataFrame(data=d)
Out:
arrival departure station train_id meetings
0 2012-05-01 01:00:00 2012-05-01 03:00:00 a 1 NaN
1 2012-05-01 03:00:00 2012-05-01 05:00:00 b 2 NaN
2 2012-05-01 06:00:00 2012-05-01 07:00:00 a 3 NaN
3 2012-05-01 04:00:00 2012-05-01 06:00:00 b 4 NaN
将数据时间转换为时间戳
df["arrival"] = df["arrival"].apply(lambda x: x.timestamp())
df["departure"] = df["departure"].apply(lambda x: x.timestamp())
初始化两个表以进行合并。
df
df2 = df
根据"站"将两个表合并在一起,以便已经比较了它们。
merge = pd.merge(df, df2, on=['station'])
table = merge[merge["train_id_x"] != merge["train_id_y"]]
arrival_x departure_x station train_id_x meetings_x arrival_y departure_y train_id_y meetings_y
1 1.335834e+09 1.335841e+09 a 1 NaN 1.335852e+09 1.335856e+09 3 NaN
2 1.335852e+09 1.335856e+09 a 3 NaN 1.335834e+09 1.335841e+09 1 NaN
5 1.335841e+09 1.335848e+09 b 2 NaN 1.335845e+09 1.335852e+09 4 NaN
6 1.335845e+09 1.335852e+09 b 4 NaN 1.335841e+09 1.335848e+09 2 NaN
现在使用比较矢量化算法
table[((table["arrival_x"] > table["arrival_y"]) & (table["arrival_x"] < table["departure_y"]) | (table["arrival_y"] > table["arrival_x"]) & (table["arrival_y"] < table["departure_x"]))]
结果:
arrival_x departure_x station train_id_x meetings_x arrival_y departure_y train_id_y meetings_y
5 1.335841e+09 1.335848e+09 b 2 NaN 1.335845e+09 1.335852e+09 4 NaN
6 1.335845e+09 1.335852e+09 b 4 NaN 1.335841e+09 1.335848e+09 2 NaN
免责声明:此算法可以进一步改进。但我希望你明白这个想法。而不是做循环,使用更快的Pandas和Numpy函数。
我的头要爆炸了。抱歉,鉴于我没有足够的积分,我无法发表评论。无论如何,我们可以让你的数据库来测试它吗?
是否必须在第 1 行与第 2 行(成对(或第 1 行与任何行匹配?
你实际上是在做O(n^2(,它肯定可以改进,但必须很好地理解问题是什么。
Pandas 中最有效的算法是试图将所有内容理解为矩阵,可以与 Numpy 数组(矢量化(进行比较和处理,但我认为事实并非如此。
"间隔"的数据类型是什么?