将 Python 中的两个电子表格与 Pandas 合并,按“时间”列中最近的“时间”,XX:XX:XX 格式的值

Posted

技术标签:

【中文标题】将 Python 中的两个电子表格与 Pandas 合并,按“时间”列中最近的“时间”,XX:XX:XX 格式的值【英文标题】:Merge two spreadsheets in Python with Pandas, by nearest 'Time' in 'Time' Column, values in XX:XX:XX format 【发布时间】:2021-02-18 15:54:21 【问题描述】:

编写我的第一个程序来组织一些数据,已经练习 python 几个月了。这个小程序将在不久的将来与我正在开发的 RFID 阅读器原型一起使用。我已经成功获取了 .txt 和 .xls,提取了相关信息,现在我想根据 XX:XX:XX 格式的时间戳来匹配它们。 .txt 是来自我的 GPS 的读数,.xls 包含来自 RFID 标签的标签信息。

我只需要根据最近的时间戳将 GPS 位置与标签 ID 配对。

请看看我到目前为止所拥有的一切,并为您提供任何指导或建议。

import numpy as np 
import pandas as pd
import xlrd

filename_xls = '11_4_TAG.xls'
df = pd.read_excel(filename_xls)
tag_data=(df[['Time', 'TagID']])
#print(tag_data)

filename = '11_4_GPS.txt'
df_gps = pd.read_csv(filename, delimiter=r"\s+", skiprows=17, skipfooter=3, engine='python', encoding="unicode_escape")
gps_data=(df_gps[['Latitude', 'Longitude', 'Time']])
#print(gps_data)

pd.merge_asof(tag_data, gps_data, on='Time', direction='nearest')
#print(pd.merge_asof)

我尝试了很多“on”、“direction”和“by”的变体

这是我的两个新数据集的输出:

标签数据

WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
        Time                     TagID
0   10:18:32  E280116060000207A633DAB6
1   10:18:57  A15427AABB00112233445566
2   10:19:07  E280116060000207A6336C96
3   10:19:09  E280116060000207A6341969
4   10:19:34  E280116060000207A633E5B9
5   10:19:40  E280116060000207A633A846
6   10:19:56  A94439112233445566778899
7   10:20:01  E200001D52120136069068C0
8   10:20:05  E280116060000207A633DA16
9   10:20:07  A63367112233445566778899
10  10:20:12  E280116060000207A633A836
11  10:20:15  E280116060000207A633CBD9
12  10:20:18  E200001D5212006106702126
13  10:20:20  A39223112233445566778899
14  10:20:28  E280116060000207A633DCC6
15  10:20:50  A02257AABB00112233445566
16  10:22:24  E280116060000207A633DA26
17  10:22:44  E280116060000207A6336AC6
18  10:23:43  E280116060000207A633DA46
19  10:24:03  E280116060000207A6336CA6
20  10:24:22  E280116060000207A633DC96
21  10:28:01  C10002AABB00112233445566
22  10:28:05  013193AABB00112233445566
23  10:28:12  017072AABB00112233445566
24  10:28:22  023764AABB00112233445566
25  10:28:42  A15800AABB00112233445566
26  10:28:49  E280116060000207A6336CC6
27  10:28:51  E280116060000207A6344236
28  10:29:00  E280116060000207A6336CB6
29  10:29:01  E280116060000207A633CBB9
30  10:29:08  E280116060000207A6341959
31  10:29:11  A72546AABB00112233445566
32  10:29:15  A93853112233445566778899
33  10:29:15  A93853AABB00112233445566
34  10:30:46  A13832AABB00112233445566
35  10:30:52  A02533AABB00112233445566
36  10:30:58  00111160600002078899CBA9
37  10:31:23  A83503AABB00112206906A73
[Finished in 0.8s]

gps_data

WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
         Latitude      Longitude      Time
0    N43°03.6205'  W085°57.5513'  10:17:46
1    N43°03.6205'  W085°57.5512'  10:17:49
2    N43°03.6203'  W085°57.5514'  10:17:51
3    N43°03.6202'  W085°57.5511'  10:17:54
4    N43°03.6199'  W085°57.5518'  10:17:57
..            ...            ...       ...
342  N43°03.6162'  W085°57.5477'  10:33:03
343  N43°03.6163'  W085°57.5472'  10:33:06
344  N43°03.6168'  W085°57.5477'  10:33:09
345  N43°03.6167'  W085°57.5477'  10:33:11
346  N43°03.6163'  W085°57.5486'  10:33:14

[347 rows x 3 columns]
[Finished in 0.8s]

错误代码:

WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
Traceback (most recent call last):
  File "C:\Users\Owner\Documents\Software\Python Programs\Data_parse\data_parse_xls.py", line 15, in <module>
    pd.merge_asof(tag_data, gps_data, on='Time', direction='nearest')
  File "C:\Users\Owner\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pandas\core\reshape\merge.py", line 563, in merge_asof
    return op.get_result()
  File "C:\Users\Owner\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pandas\core\reshape\merge.py", line 1483, in get_result
    join_index, left_indexer, right_indexer = self._get_join_info()
  File "C:\Users\Owner\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pandas\core\reshape\merge.py", line 884, in _get_join_info
    (left_indexer, right_indexer) = self._get_join_indexers()
  File "C:\Users\Owner\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pandas\core\reshape\merge.py", line 1789, in _get_join_indexers
    return func(left_values, right_values, self.allow_exact_matches, tolerance)
  File "pandas\_libs\join.pyx", line 966, in pandas._libs.join.__pyx_fused_cpdef
TypeError: No matching signature found
[Finished in 0.8s]

【问题讨论】:

您确定要按时合并吗?因为这些时间必须完全匹配才能合并 这是我目前仅有的两个电子表格共有的两个数据点。我正在捕获 GPS 数据同时还捕获标签数据,我正在开发的原型将在标签读取时捕获 GPS 信息。我想那时我不再需要这个软件了。阅读“熊猫”,他们谈论找到最接近的整数,我相信它可以是日期时间、时间戳或整数。我无法让它工作。 【参考方案1】:

正如@MhDG7 所说,您只能合并完全匹配的值。因此,您的第一个任务是为 gps_data 中的每个时间戳找到 tag_data 中的时间戳之间最接近的匹配项。下面的关键行是这个

tag_secs_for_gps_secs = [min(tag_data['Secs'], key = lambda tag_secs: abs(tag_secs - gps_secs))  for gps_secs in gps_data['Secs'] ]

正是这样做的。在这里,对于以秒表示的每个 gps 时间(见下文),我们在 tag_data 时间戳(也以秒表示)中找到一个元素,该元素最小化两者之间的绝对距离

这是完整的脚本 首先我们创建玩具数据

import sys
import pandas as pd
from io import StringIO
from datetime import datetime

gps_data_raw = StringIO(
    """
Latitude,Longitude,Time
N43°03.6162',W085°57.5477',10:33:03
N43°03.6163',W085°57.5472',10:33:06
N43°03.6168',W085°57.5477',10:33:09
N43°03.6167',W085°57.5477',10:33:11
N43°03.6163',W085°57.5486',10:33:14
    """)
gps_data = pd.read_csv(gps_data_raw)

tag_data_raw = StringIO(
    """
Time,TagID
10:33:01,C10002AABB00112233445566
10:33:05,013193AABB00112233445566
10:33:12,017072AABB00112233445566
10:33:22,023764AABB00112233445566
10:33:42,A15800AABB00112233445566
10:33:49,E280116060000207A6336CC6
10:33:51,E280116060000207A6344236
    """)
tag_data = pd.read_csv(tag_data_raw)

然后我们将时间戳转换为日期时间对象,并最终转换为从 base_date 开始的(整数)秒

base_date = datetime(1900, 1, 1, 0, 0, 0)
gps_data['Time'] = pd.to_datetime(gps_data['Time'], format='%H:%M:%S')
tag_data['Time'] = pd.to_datetime(tag_data['Time'], format='%H:%M:%S')
gps_data['Secs'] = gps_data['Time'].apply(lambda t: int((t-base_date).total_seconds()))
tag_data['Secs'] = tag_data['Time'].apply(lambda t: int((t-base_date).total_seconds()))

然后我们匹配时间戳,将它们粘贴到数据框中,并在该列上合并

tag_secs_for_gps_secs = [min(tag_data['Secs'], key = lambda tag_secs: abs(tag_secs - gps_secs))  for gps_secs in gps_data['Secs'] ]

gps_data['Nearest_tag_secs'] = tag_secs_for_gps_secs
merged_data = gps_data.merge(tag_data, left_on = 'Nearest_tag_secs', right_on = 'Secs')

resultimg 数据框merged_data 可以进行一些清理,但在这里完整复制,以便您查看发生了什么

|    | Latitude     | Longitude     | Time_x              |   Secs_x |   Nearest_tag_secs | Time_y              | TagID                    |   Secs_y |
|---:|:-------------|:--------------|:--------------------|---------:|-------------------:|:--------------------|:-------------------------|---------:|
|  0 | N43°03.6162' | W085°57.5477' | 1900-01-01 10:33:03 |    37983 |              37981 | 1900-01-01 10:33:01 | C10002AABB00112233445566 |    37981 |
|  1 | N43°03.6163' | W085°57.5472' | 1900-01-01 10:33:06 |    37986 |              37985 | 1900-01-01 10:33:05 | 013193AABB00112233445566 |    37985 |
|  2 | N43°03.6168' | W085°57.5477' | 1900-01-01 10:33:09 |    37989 |              37992 | 1900-01-01 10:33:12 | 017072AABB00112233445566 |    37992 |
|  3 | N43°03.6167' | W085°57.5477' | 1900-01-01 10:33:11 |    37991 |              37992 | 1900-01-01 10:33:12 | 017072AABB00112233445566 |    37992 |
|  4 | N43°03.6163' | W085°57.5486' | 1900-01-01 10:33:14 |    37994 |              37992 | 1900-01-01 10:33:12 | 017072AABB00112233445566 |    37992 |

【讨论】:

非常感谢。我今天将深入研究并玩弄。我明白你们现在所说的关于合并相同值的内容。当我在我的露营者周围走动并阅读标签数据时,读数会持续几分钟。我也需要在几分钟内比赛,但我现在肯定可以看到实现这一目标的途径。我从来没有意识到你必须有多么有创意才能编写代码,真的很享受它。谢谢你们。 没问题。要清楚我的代码匹配自基准日期以来的 total 秒(因此任何时间戳的唯一数字),而不仅仅是时间戳的最后两位数 哦,太棒了,我尝试将“时间戳”转换为某种整数,但没有成功,很高兴看到我昨晚走在了正确的轨道上。我稍后会更新进度。 谢谢你,这很好!我将研究/谷歌搜索此代码的部分内容以完全理解,但我会鸟瞰正在发生的事情。我首先复制并粘贴了一小部分原始数据以使其正确,现在我将按照您所描绘的方式将我的两个电子表格填充到 StringIO 中。 @Beasmazter, SrtingIO 东西只有一个独立的示例,它不依赖于任何文件,并且您在最终解决方案中并不真正需要该部分。您的 df = pd.read_excel(filename_xls) 应该可以正常工作(好吧,您可能需要注意值周围的空白,但除此之外应该没有问题)。

以上是关于将 Python 中的两个电子表格与 Pandas 合并,按“时间”列中最近的“时间”,XX:XX:XX 格式的值的主要内容,如果未能解决你的问题,请参考以下文章

Pandas与openpyxl库的超强结合,再见,Excel!

Pandas与openpyxl库的超强结合,再见,Excel!

如何在 pandas/python 中查看 excel 电子表格的公式?

从 Pandas/Python 中的选定单元格访问索引/行/列

我想要一个 pandas 脚本根据第一个电子表格中的值将一个 excel 表中的值排列到另一个表中

使用 pandas-Python 3 遍历 excel 中的行和列