Numpy:如何在多对值之间提取 numpy 数组的行?
Numpy: how to extract rows of numpy array between multiple pairs of values?
我对 Python 比较陌生,目前我在以有效的方式实现概念上简单的算法方面遇到了一些问题。
我已经能够在 pandas 中完成(但执行起来很慢)。
我有一个由 n 行和 3 列组成的 ndarray:
--------------------
"A" | 1 | 12
--------------------
"B" | 2 | 34
--------------------
"S" | 3 | 1
--------------------
"B" | 4 | 145
--------------------
"A" | 5 | 132
--------------------
"B" | 6 | 234
--------------------
"E" | 7 | 1
--------------------
"B" | 8 | 15
--------------------
第一列表示 id,第二列表示 timestamp,第三列表示 value.
我必须过滤 ndarray,只取时间戳包含在 id "S"(开始)时间戳和 id "E"(结束)时间戳之间的行。
在同一个 ndarray 中可能有超过一对 "S" 和 "E"。在不连续的 "S" 和 "E" 对的情况下,我需要最短的子数组。换句话说,输出中不应出现 id "S" 或 "E"。
所以输出应该是:
--------------------
"B" | 4 | 145
--------------------
"A" | 5 | 132
--------------------
"B" | 6 | 234
--------------------
如前所述,我使用pandas得到了这个结果,但是这个函数真的又长又复杂,执行起来也很慢。所以我确信使用 numpy 可以获得更好和最有效的算法。
你有什么想法吗?
提前致谢。
编辑
这是使用pandas获得预期结果的代码片段。
"Intel Core i7-6820 @ 2.70GHz" 处理器的执行时间约为 0.015 秒。
df = pd.DataFrame({'id': ['A', 'B', 'C', 'S', 'C', 'C', 'A', 'B', 'E', 'A', 'C', 'B', 'B', 'S', 'C', 'A', 'E', 'B'],
't': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
'v': [145, 543, 12, 1, 14, 553, 65, 657, 1, 32, 54, 22, 11, 1, 6, 22, 1, 4]})
print(df)
res = pd.DataFrame()
id = "id"
t = "t"
v = "v"
id_bit_start = "S"
id_bit_end = "E"
# taking only "S" and "E" from df (when their value is 1)
df_data_bit = df.loc[
((df[id] == id_bit_start) |
(df[id] == id_bit_end)) &
(df[v] == 1.0)
]
# do something only if at least one "S" is present
if id_bit_start in df_data_bit[id].values:
# creating empty list of time windows
t_windows = list()
t_wind_temp = [None, None]
# for each bit "S" or "E"
for index, bit in df_data_bit.iterrows():
# if it is a "S"
if bit[id] == id_bit_start:
# set the start of the time window
t_wind_temp[0] = bit[t]
# if it is a "E" and the "S" has already been processed
elif t_wind_temp[0] is not None:
# set the end of the time window
t_wind_temp[1] = bit[t]
# append the current time window to our list
t_windows.append(t_wind_temp)
# reset the current time window
t_wind_temp = [None, None]
# taking everything but "S" and "E"
df_data = df.loc[
~((df[id] == id_bit_start) |
(df[id] == id_bit_end))
]
# for each created time window
for t_window in t_windows:
# take only data with timestamps between the time window
result = df_data.loc[
(df_data[t] >= t_window[0])
&
(df_data[t] <= t_window[1])
]
# append to the final result
res = pd.concat([res, result])
print(res)
这解决了您对 S 和 E 连续性的不确定性:
假设您的时间戳按升序排列:
import re
a = df.to_records(index=False)
idx = [m.span() for m in re.finditer('S[^{SE}]*?E', ''.join(a['id']))]
indexer = np.r_[tuple([np.s_[i+1:j-1] for (i,j) in idx])]
a_filtered = a[indexer]
解释:
快速计算这个有一些技巧:
- 将您的数据框转换为结构化数组
- 将所有id字符转换为字符串
- 查找
S.*?E
的非贪婪匹配(请注意,如果您的 ID 不是单个字母,您可以将 S 和 E 更改为任何子字符串)
- 获取您找到的子字符串的开始和结束索引
- 创建 4
中这些索引之间的所有索引的列表
- 使用 5
中的索引过滤数组
我对 Python 比较陌生,目前我在以有效的方式实现概念上简单的算法方面遇到了一些问题。 我已经能够在 pandas 中完成(但执行起来很慢)。
我有一个由 n 行和 3 列组成的 ndarray:
--------------------
"A" | 1 | 12
--------------------
"B" | 2 | 34
--------------------
"S" | 3 | 1
--------------------
"B" | 4 | 145
--------------------
"A" | 5 | 132
--------------------
"B" | 6 | 234
--------------------
"E" | 7 | 1
--------------------
"B" | 8 | 15
--------------------
第一列表示 id,第二列表示 timestamp,第三列表示 value. 我必须过滤 ndarray,只取时间戳包含在 id "S"(开始)时间戳和 id "E"(结束)时间戳之间的行。 在同一个 ndarray 中可能有超过一对 "S" 和 "E"。在不连续的 "S" 和 "E" 对的情况下,我需要最短的子数组。换句话说,输出中不应出现 id "S" 或 "E"。
所以输出应该是:
--------------------
"B" | 4 | 145
--------------------
"A" | 5 | 132
--------------------
"B" | 6 | 234
--------------------
如前所述,我使用pandas得到了这个结果,但是这个函数真的又长又复杂,执行起来也很慢。所以我确信使用 numpy 可以获得更好和最有效的算法。
你有什么想法吗?
提前致谢。
编辑
这是使用pandas获得预期结果的代码片段。 "Intel Core i7-6820 @ 2.70GHz" 处理器的执行时间约为 0.015 秒。
df = pd.DataFrame({'id': ['A', 'B', 'C', 'S', 'C', 'C', 'A', 'B', 'E', 'A', 'C', 'B', 'B', 'S', 'C', 'A', 'E', 'B'],
't': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
'v': [145, 543, 12, 1, 14, 553, 65, 657, 1, 32, 54, 22, 11, 1, 6, 22, 1, 4]})
print(df)
res = pd.DataFrame()
id = "id"
t = "t"
v = "v"
id_bit_start = "S"
id_bit_end = "E"
# taking only "S" and "E" from df (when their value is 1)
df_data_bit = df.loc[
((df[id] == id_bit_start) |
(df[id] == id_bit_end)) &
(df[v] == 1.0)
]
# do something only if at least one "S" is present
if id_bit_start in df_data_bit[id].values:
# creating empty list of time windows
t_windows = list()
t_wind_temp = [None, None]
# for each bit "S" or "E"
for index, bit in df_data_bit.iterrows():
# if it is a "S"
if bit[id] == id_bit_start:
# set the start of the time window
t_wind_temp[0] = bit[t]
# if it is a "E" and the "S" has already been processed
elif t_wind_temp[0] is not None:
# set the end of the time window
t_wind_temp[1] = bit[t]
# append the current time window to our list
t_windows.append(t_wind_temp)
# reset the current time window
t_wind_temp = [None, None]
# taking everything but "S" and "E"
df_data = df.loc[
~((df[id] == id_bit_start) |
(df[id] == id_bit_end))
]
# for each created time window
for t_window in t_windows:
# take only data with timestamps between the time window
result = df_data.loc[
(df_data[t] >= t_window[0])
&
(df_data[t] <= t_window[1])
]
# append to the final result
res = pd.concat([res, result])
print(res)
这解决了您对 S 和 E 连续性的不确定性:
假设您的时间戳按升序排列:
import re
a = df.to_records(index=False)
idx = [m.span() for m in re.finditer('S[^{SE}]*?E', ''.join(a['id']))]
indexer = np.r_[tuple([np.s_[i+1:j-1] for (i,j) in idx])]
a_filtered = a[indexer]
解释:
快速计算这个有一些技巧:
- 将您的数据框转换为结构化数组
- 将所有id字符转换为字符串
- 查找
S.*?E
的非贪婪匹配(请注意,如果您的 ID 不是单个字母,您可以将 S 和 E 更改为任何子字符串) - 获取您找到的子字符串的开始和结束索引
- 创建 4 中这些索引之间的所有索引的列表
- 使用 5 中的索引过滤数组