【问题标题】:How to join two dataframes for which column time values are within a certain range and are not datetime or timestamp objects?如何连接列时间值在一定范围内且不是日期时间或时间戳对象的两个数据框?
【发布时间】:2019-07-17 19:32:40
【问题描述】:

我有两个数据框,如下所示:

     time browncarbon blackcarbon
 181.7335    0.105270         NaN
 181.3809    0.166545    0.001217
 181.6197    0.071581         NaN

 422 rows x 3 columns

   start       end    toc 
179.9989  180.0002  155.0
180.0002  180.0016  152.0
180.0016  180.0030  151.0

1364 rows x 3 columns

第一个数据帧有一个时间列,每四分钟有一个瞬间。第二个数据帧有两个时间列,每两分钟间隔一次。这两个时间列不会同时开始和结束。但是,它们包含在同一天收集的数据。我怎样才能制作另一个包含以下内容的数据框:

time browncarbon blackcarbon toc

422 rows X 4 columns

但是,Stack Overflow 上有一个相关的答案,它仅适用于时间列是日期时间或时间戳对象的情况。链接为:How to join two dataframes for which column values are within a certain range?

附录1:封装到时间行之一的多个开始行和结束行也应该对应一个目录行,就像现在一样,但是,它应该是多个目录行的平均值,即目前不是这样。

附录2:Merging two pandas dataframes with complex conditions

【问题讨论】:

  • 如何填写toc 列?时间在哪里?
  • 而且,您的示例数据并不能真正反映您的描述。例如,我看不到第一个数据帧是如何每 4 分钟间隔一次的。
  • 我同意,这不会给出任何结果,因为在您的示例中,没有时间落在第二个数据帧的开始和结束之间。更改您的示例数据框,使第一个数据框的任何 time 值都落在第二个数据框的范围内
  • @QuangHoang 是的,应该在时间范围内填写 toc 列。
  • @QuangHoang 时间格式特殊。整数部分代表一年中的哪一天。小数部分表示自最后一天结束以来经过的秒数。例如,下午 12:00 将是 12*3600/86400。

标签: python-3.x pandas


【解决方案1】:

我们创建一个人工键列来执行outer merge 以获取笛卡尔积(行之间的所有匹配项)。然后我们过滤所有时间在.query范围之间的行。

注意:我编辑了一行的值,以便我们可以得到一个匹配项(参见底部示例数据帧中的第 0 行)

df1.assign(key=1).merge(df2.assign(key=1), on='key', how='outer')\
   .query('(time >= start) & (time <= end)')\
   .drop(['key', 'start', 'end'], axis=1)

输出

       time  browncarbon  blackcarbon    toc
1  180.0008      0.10527          NaN  152.0

使用的示例数据框:

df1:

       time  browncarbon  blackcarbon
0  180.0008     0.105270          NaN
1  181.3809     0.166545     0.001217
2  181.6197     0.071581          NaN

df2:

      start       end    toc
0  179.9989  180.0002  155.0
1  180.0002  180.0016  152.0
2  180.0016  180.0030  151.0

【讨论】:

  • 多么好的答案!我傻了。
  • 谢谢,希望对你有所帮助:) @SujaiBanerji
  • 但是,我得到的行数超出了需要。当我运行你的代码时,我得到了 455 行 x 4 列。这怎么可能?
  • 我在&gt;=&lt;= 中使用了包含边界。玩转&gt;&lt; 或@SujaiBanerji 的任何其他组合。这就是为什么包含有代表性的示例数据很重要。
  • 有趣的是,如果我删除任何一个等号,我会得到 421 行 x 4 列。
【解决方案2】:

由于开始和结束间隔是互斥的,我们可以在 df2 中创建新列,使其包含 floor(start) 和 floor(end) 范围内的所有整数值。稍后,在 df1 中添加另一列作为 floor(time),然后在 df1 和 df2 上进行左外连接。我认为应该这样做,但如果需要,您可能必须删除 nan 值和额外的列。如果你把 csv 文件发给我,我也许可以把脚本发给你。我希望我回答了你的问题。

【讨论】:

  • 二凡已经回答了这个问题。不过,非常感谢您回答问题。
【解决方案3】:

也许您可以将列转换为时间戳,然后在您链接的其他问题中使用答案

from pandas import Timestamp
from dateutil.relativedelta import relativedelta as rd

def to_timestamp(x):
    return Timestamp(2000, 1, 1) + rd(days=x)

df['start_time'] = df.start.apply(to_timestamp)
df['end_time'] = df.end.apply(to_timestamp)

【讨论】:

    【解决方案4】:

    您的第二个数据框太短,因此无法反映有意义的合并。所以我稍微修改了一下:

    df2 = pd.DataFrame({'start': [179.9989, 180.0002, 180.0016, 181.3, 181.5, 181.7],
     'end': [180.0002, 180.0016, 180.003, 181.5, 185.7, 181.8],
     'toc': [155.0, 152.0, 151.0, 150.0, 149.0, 148.0]})
    
    df1['Rank'] = np.arange(len(df1))
    
    new_df = pd.merge_asof(df1.sort_values('time'), df2,
              left_on='time',
              right_on='start')
    

    给你:

           time  browncarbon  blackcarbon  Rank  start    end    toc
    0  181.3809     0.166545     0.001217     1  181.3  181.5  150.0
    1  181.6197     0.071581          NaN     2  181.5  185.7  149.0
    2  181.7335     0.105270          NaN     0  181.7  181.8  148.0
    

    您可以在Rank 上删除额外的列和sort_values。例如:

    new_df.sort_values('Rank').drop(['Rank','start','end'], axis=1)
    

    给予:

           time  browncarbon  blackcarbon    toc
    2  181.7335     0.105270          NaN  148.0
    0  181.3809     0.166545     0.001217  150.0
    1  181.6197     0.071581          NaN  149.0
    

    【讨论】:

    • 其实你的答案就是正确的答案。但是,如果您能将我在附录中陈述的条件包括在内,我将更加感激您。
    猜你喜欢
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-06
    • 2013-04-14
    相关资源
    最近更新 更多