如何列出属于一组范围内的所有数字对?
Posted
技术标签:
【中文标题】如何列出属于一组范围内的所有数字对?【英文标题】:How to list all the pairs of numbers which fall under a group of range? 【发布时间】:2018-11-19 06:39:03 【问题描述】:假设我有数据框 df1,其中包括两列 - A 和 B。A 的值代表下限,B 的值代表上限。
A B
10.5 20.5
30.5 40.5
50.5 60.5
我有另一个数据框,其中包含两列 - C 和 D 包含不同范围的数字。
C D
12.34 15.90
13.68 19.13
33.5 35.60
35.12 38.76
50.6 59.1
现在我想列出 df2 中属于 df1 中的组(在下限和上限之间)的所有对。
最终输出应该是这样的 -
Key Values
(10.5, 20.5) [(12.34, 15.90), (13.68, 19.13)]
(30.5, 40.5) [(33.5, 35.60), (35.12, 38.76)]
(50.5, 60.5) [(50.6, 59.1)]
该解决方案应该是有效的,因为我有 5000 组范围和来自不同范围的 85000 个数字。
【问题讨论】:
这段代码应该对性能有多重要? “高效”是什么意思? 【参考方案1】:试试下面的代码:
df = pd.DataFrame()
df['Key'] = [(row['A'],row['B']) for idx,row in df1.iterrows()]
values_col_test = [(c1,c2) for c1,c2 in df2.itertuples(index=False)]
values_col = []
for i in range(0,len(values_col_test),2):
try:
values_col.append(list(values_col_test[i:i+2]))
except:
values_col.append(list(values_col_test[i]))
df['Value'] = values_col
print(df)
输出:
Key Value
0 (10.5, 20.5) [(2.34, 11.9), (3.68, 19.13)]
1 (30.5, 40.5) [(33.5, 35.6), (35.12, 38.76)]
2 (50.5, 60.5) [(50.6, 59.1)]
【讨论】:
它给出了错误ValueError: Length of values does not match length of index
@AbdullahAlImran 现在我编辑了我的答案,使其看起来更像所需的输出。但是为什么它对你不起作用?它对我来说很好用,我使用 python 3.6.0 和 pandas 0.19.2【参考方案2】:
它在我的电脑上并不快(约 30 秒),但如果您有多个内核,可以使用 multiprocessing
包轻松加速。
生成数据:
def get_fake(n):
df = pd.DataFrame(np.random.rand(n * 2).reshape(-1, 2))
df.loc[:, 1] += 1
return df
df1 = get_fake(200)
df2 = get_fake(90000)
然后是处理部分:
from collections import defaultdict
result = defaultdict(list)
for index, start, stop in df1.itertuples():
subdf = df2[(start < df2.iloc[:, 0]) & (df2.iloc[:, 1] < stop)]
result[(start, stop)] += subdf.values.tolist()
Result 是一个 dict,但如有必要,可以轻松转换为 Series。
【讨论】:
它会产生错误TypeError: cannot do label indexing on <class 'pandas.core.indexes.base.Index'> with these indexers [0] of <class 'int'>
@AbdullahAlImran 已更正(将 loc
更改为 iloc
)。它适用于我的假数据,因为列用整数标记【参考方案3】:
一种解决方案是使用apply
,例如:
# first create your output DF with the keys from your df with A and B
df = pd.DataFrame('Key':[(a,b) for a,b in df1.itertuples(index=False)])
# define a function to find the range in df2 within the range from the Keys column
def find_range( key, df_2):
mask = (key[0] <= df_2['C']) & (key[1] >= df_2['D'])
return [(c,d) for c,d in df_2[mask].itertuples(index=False)]
#now create the column Values with apply
df['Values'] = df['Key'].apply(find_range, args=(df2,))
# output
Key Values
0 (10.5, 20.5) [(12.34, 15.9), (13.68, 19.13)]
1 (30.5, 40.5) [(33.5, 35.6)]
注意:我假设在您的数据中列 C 始终低于 D,如果不是,您必须更改函数中的掩码以检查 C 和 D 是否在 key[0] 和 key[1] 内。另外,我没有收到您的所有输入,因此第 1 行的值与您显示的不同,只是输入不同。
【讨论】:
【参考方案4】:如果你使用interval index
会很容易,即
idx = pd.IntervalIndex.from_arrays(df['A'],df['B'])
keys = df.values.tolist()
values = df2.groupby(df.loc[idx.get_indexer(df2['C'])].index).apply(lambda x : x.values)
new_df = pd.DataFrame('key': keys , 'value': values)
key value
0 [10.5, 20.5] [[12.34, 15.9], [13.68, 19.13]]
1 [30.5, 40.5] [[33.5, 35.6], [35.12, 38.76]]
2 [50.5, 60.5] [[50.6, 59.1]]
根据区间索引访问数据将为您提供密钥,以便您可以分组和聚合,即
df.loc[idx.get_indexer(df2['C'])]
A B
0 10.5 20.5
0 10.5 20.5
1 30.5 40.5
1 30.5 40.5
2 50.5 60.5
【讨论】:
谢谢。但它会产生错误ValueError: cannot handle non-unique indices
@AbdullahAlImran 告诉我,如果“C”和“D”属于“A”和“B”两个不同的组,结果应该是什么。 ?
然后你可以放弃其他组,只拿一组。
我现在不在我的笔记本电脑附近。只需从“A”和“B”列中删除重复项,然后解决方案就可以正常工作。以上是关于如何列出属于一组范围内的所有数字对?的主要内容,如果未能解决你的问题,请参考以下文章