假设我有两个 pandas 数据框:第一个数据框包含代码和时间上的一些数据(反映在下面示例中的 Col1 和 Col2 列中)。 第二个数据框详细说明了一组日期范围。这个想法是排除日期落在任何边界(在
df_table
中)之间的任何行(在 df_dates
中)。每个代码都有一个或多个(且不同的)界限,或者可能根本没有界限。
下面的代码运行良好并正确完成任务,但它花费了太多时间(第一个数据帧df_table
有几百万行)。
import pandas as pd
import numpy as np
def filter_date(main_table, table_dates):
idx = table_dates.Code == main_table.Code
table_dates = table_dates.loc[idx]
filtering = (table_dates['Start Date'] <= main_table.Timestamp) & (table_dates['End Date'] >= main_table.Timestamp)
return filtering.any()
# Example below
df_table = pd.DataFrame({'Code':['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'C', 'C'], 'Timestamp':[1,2,3,4,5,6,1,2,3,4,5,6,1,2], 'Col1':np.arange(14), 'Col2':np.arange(14)})
df_dates = pd.DataFrame({'Code':['A', 'A', 'B'], 'Start Date':[1, 5, 1], 'End Date':[2, 6, 3]})
df_table['Exclude Date'] = df_table.apply(lambda x: filter_date(x, df_dates), axis=1)
df_table = df_table[df_table['Exclude Date'] == False]
print(df_table)
有没有更聪明的方法来做到这一点并避免这种情况
.apply()
?
你可以尝试这样的事情:
1.创建一个字典,其中包含每个代码的范围
2.检查时间戳值是否存在于该代码的范围内
3.添加包含“是”和“否”值的列。然后您可以在此基础上对数据框进行分组。
看起来更长,但检查字典中列表的值更快。
import pandas as pd
import numpy as np
# Example below
df_table = pd.DataFrame({'Code':['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'C', 'C'], 'Timestamp':[1,2,3,4,5,6,1,2,3,4,5,6,1,2], 'Col1':np.arange(14), 'Col2':np.arange(14)})
df_dates = pd.DataFrame({'Code':['A', 'A', 'B'], 'Start Date':[1, 5, 1], 'End Date':[2, 6, 3]})
#Create Dictionary with boundary ranges for each code value
ranges = {k: [] for k in df_dates['Code'].unique()}
for i in range(len(df_dates)):
row = df_dates.loc[i]
ranges[row['Code']].append((row['Start Date'], row['End Date']))
#Check if 'Timestamp' values are in the range and add a 'Yes, 'No' and 'NaN' column
def check_range(value, boundary):
if any(lower <= value <= upper for (lower, upper) in boundary):
return 'Yes'
else:
return 'No'
#Check Time Values here
within_boundary = []
for i in range(len(df_table)):
row = df_table.loc[i]
if row['Code'] in (ranges.keys()):
if any(lower <= row['Timestamp'] <= upper for (lower, upper) in ranges[row['Code']]):
within_boundary.append('Yes')
else:
within_boundary.append('No')
else:
within_boundary.append('NaN')
df_table['Within_Boundary'] = within_boundary
print(df_table)
输出:
Code Timestamp Col1 Col2 Within_Boundary
0 A 1 0 0 Yes
1 A 2 1 1 Yes
2 A 3 2 2 No
3 A 4 3 3 No
4 A 5 4 4 Yes
5 A 6 5 5 Yes
6 B 1 6 6 Yes
7 B 2 7 7 Yes
8 B 3 8 8 Yes
9 B 4 9 9 No
10 B 5 10 10 No
11 B 6 11 11 No
12 C 1 12 12 NaN
13 C 2 13 13 NaN