【问题标题】:Pandas: Alternative methods to using nested for-loops for cell-comparisonsPandas:使用嵌套 for 循环进行单元比较的替代方法
【发布时间】:2020-07-01 12:31:04
【问题描述】:

我正在解决一个问题,试图在车站找到火车会议,但我很难找到一种方法来进行必要的比较而不使用嵌套的 for 循环(这会很慢,我有数十万个数据点)。

我的 DataFrame-rows 包含以下有用数据:到达时间(日期时间)、出发时间(日期时间)、唯一的火车 ID(字符串)、火车在开始和结束时间之间所在的车站(字符串)和它遇到的火车的火车 ID 的单元格(字符串,开始时为空)。我想找到所有满足的行对,这意味着我想要满足:

  1. 从第 1 行的到达到离开的时间间隔与第 2 行的到达到离开的时间间隔重叠。
  2. 位于同一个车站。

此外,没有超过两列火车的会议。

我尝试了以下方法(代码如下):我根据到达和离开时间创建了间隔对象。然后我使用嵌套的 for 循环将每一行的间隔与其他每一行进行比较,如果它们重叠,我检查站是否匹配。如果他们这样做了,我将每个 train-ID 存储在另一个的 train-meeting-cell 中。

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 对象的情况下更快地完成,我很乐意使用它。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    初始化数据帧。

    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
    

    免责声明:此算法可以进一步改进。但我希望你能明白。代替 for 循环,使用更快的 Pandas 和 Numpy 函数。

    【讨论】:

    • 这太棒了!我不知道我可以用这种方式与 Merge 一起编写矢量化比较,所以这确实为进一步调查打开了大门,同时也解决了我的问题。这是我项目的一个突破,感谢你们!
    【解决方案2】:

    我的头要爆炸了。抱歉,由于我没有足够的积分,我无法发表评论。无论如何,我们可以让您的数据库对其进行测试吗?

    第 1 行是否必须与第 2 行(对)或第 1 行与任意行匹配?

    你实际上在做 O(n^2),它肯定可以改进,但它必须很好地理解问题是什么。

    Pandas 中最有效的算法是试图将所有内容理解为一个矩阵,可以与 Numpy 数组(向量化)进行比较和处理,但我认为情况并非如此。

    “区间”的数据类型是什么?

    【讨论】:

    • 对问题进行了编辑,希望能回答一些问题。匹配是针对任何一对行完成的,而不仅仅是相邻的行。
    • 完成 :) 希望对您有所帮助
    猜你喜欢
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 2023-02-05
    相关资源
    最近更新 更多