Python:在特定位置将行插入DataFrame的更快捷方式?

时间:2018-02-19 14:07:01

标签: python python-3.x pandas dataframe

我有一个约40,000行的DataFrame。 DataFrame看起来大致如下:

             Unix Time                           UTC  Val. 1  Val. 2  Val. 3
1    1518544176.927486    2018-02-13 17:49:36.927486    5.00    0.25    2.00
2    1518544176.929897    2018-02-13 17:49:36.929897    4.50    1.00    3.00
3    1518544176.932310    2018-02-13 17:49:36.932310    4.00    0.75    1.75
...

第0列,第2-4列是<class 'numpy.float64'>类型。第1列是<class 'pandas._libs.tslib.Timestamp'>类型。当根据时间绘制任何数据列时,我们会看到一个波形。但是,收购中偶尔会出现中断。例如,我们可能有:

               Unix Time                           UTC  Val. 1  Val. 2  Val. 3
576    1518544181.755085    2018-02-13 17:49:41.755085    0.10    0.01    0.93
577    1518544182.041129    2018-02-13 17:49:42.041129    0.11    0.02    0.95
...

可以看出,读数576和577之间存在~0.3秒的差距。问题是,在绘制数据时,即使没有数据,matplotlib也会连接点。这个“问题”的解决方案已经在Stack Overflow和在线广告的其他问题中得到解决,虽然我不喜欢......好吧,其中任何一个,最好的选择似乎是将NaN插入到数据差距。由于matplotlib没有绘制NaNs,所以它是一种偷偷摸摸的方法来欺骗它使你的情节变得更加真实。

要做到这一点,我首先找到前两个读数之间的时间差(这是安全的),并使用两倍的值作为我的指标“是否有差距?”然后我遍历DataFrame,检查差距。找到一个后,我在数据列中创建一个临时的NaN行,并在时间列的采集间隙中间创建时间值。然后我修改一个由旧的DataFrame组成的新DataFrame,加上这一行。这可以在这里看到:

df2 = df.copy()
for i, row in df.iterrows():
    # The following code checks the delta-t of all timestamp pairs.
    # We have i > 0 because it can't activate on the first entry.
    if i > 0:
        delta_t_unix = row['Unix Time'] - prev_timestamp_unix
        delta_t_utc = row['UTC'] - prev_timestamp_utc
        # If delta_t_unix > the allowed data gap, add new timestamps and NaNs.
        if delta_t_unix > allowed_gap:
            time_unix = row['Unix Time'] - (delta_t_unix / 2.0)
            time_utc = row['UTC'] - (delta_t_utc / 2.0)
            val1 = np.nan
            val2 = np.nan
            val3 = np.nan
            new_row = pd.DataFrame({'Unix Time': time_unix, 'UTC': time_utc,
                                    'Val. 1': val1, 'Val. 2': val2,
                                    'Val. 3': val3}, index = [i])
            df2 = pd.concat([df2.ix[:i-1], new_row,
                            df2.ix[i:]]).reset_index(drop = True)
    # Set the previous timestamp for use in the beginning of the loop.
    prev_timestamp_unix = row[timestamp_unix]
    prev_timestamp_utc = row[timestamp_utc]
# Make the final DataFrame with the completed lists.
df2 = df2[['Unix Time', 'UTC', 'Val. 1', 'Val. 2', 'Val. 3']]

目前这需要大约4.5秒,这要感谢this question(过去需要大约6.5,因为我愚蠢地迭代并创建每个列的新列表,然后创建一个新的DataFrame)。然而,这仍然比我预期或更喜欢的要慢得多。有没有人对如何提高速度有任何想法?我对Pandas和DataFrames还是一个新手,所以我相信这可能会更好。谢谢!

编辑:值得一提的是,如果我删除datetime列,则会将时间分成两半(不幸的是,我无法在实践中将其删除)。

2 个答案:

答案 0 :(得分:2)

您可以使用以下内容重新采样到2.4ms:

df['utc_time'] = pd.to_datetime(df['utc_time'])
df.set_index(df['utc_time'])[['val1','val2','val3']].resample('2.4ms', loffset='1.2ms').mean().reset_index()

答案 1 :(得分:1)

可以加快速度的事情:

  1. 使用df.itertuples()代替df.iterrows(),这可能会稍微改善执行时间。
  2. 如果您可以发布改进,我会很感激,但根据我的经验,我测试的情况有很大差异(循环内更简单的指令提高了10倍)。

    1. 不是使用pd.concat将行放在一起,而是创建一个元组列表,然后仅在此列表中的循环之后生成一个DataFrame。

      for i, unix_time, utc_time, val1, val2, val3 in df.itertuples():
      
          list_of_values = []
          if i > 0:
              delta_t_unix = unix_time - prev_timestamp_unix
              delta_t_utc = utc_time - prev_timestamp_utc
      
              if delta_t_unix > allowed_gap:
                  new_time_unix = unix_time - (delta_t_unix / 2.0)
                  new_time_utc = utc_time - (delta_t_utc / 2.0)
                  list_of_values.append((new_time_unix, new_time_utc, np.nan, np.nan, np.nan))
      
          # Set the previous timestamp for use in the beginning of the loop.
          prev_timestamp_unix = unix_time 
          prev_timestamp_utc = utc_time 
      
          list_of_values.append(((unix_time , utc_time, val1, val2, val3))
      
      df2 = pd.DataFrame(list_of_values, columns=['Unix Time', 'UTC', 'Val. 1', 'Val. 2', 'Val. 3'])
      
    2. 这可能会加速很多事情。