Python:如何检查值列表是否包含在一个范围内
Python: how to check if a list of values is contained within a range
我正在处理一个结构为数据框的值列表,我想将此列表的每个值与另一个数据框(有点像下面的这个)进行比较:
Name Start End
Blue 10 28
Red 23 25
Green 89 107
Purple 168 216
Yellow 21 40
现在假设值列表是这样的:
Name Value
W 37
X 176
Y 43
Z 96
对于“值”列中的每个元素,我想检查该值是否包含在第一个数据帧的每个“开始”-“结束”范围内,并将其添加到列表中(即 contained = []
).在示例中 W (37)
包含在 Yellow
中, X (176)
包含在 Purple
中, Z (96)
包含在 Green
中,而 Y
不匹配所以它将被排除(或添加到另一个列表 not_contained = []
)。
我怎样才能做到这一点?谢谢大家
因为在 sql 中的运算符连接之间没有很好的选择,或者您可以在临时键上交叉连接它们然后过滤结果。
df['key'] = 0
df2['key'] = 0
df.merge(df2, on='key', how='outer').query('Value >= Start & Value <= End').pop('Name_y').tolist()
作为松散的解决方案,您可以遍历一行并匹配值。我已经使用上述方法创建了一个解决方案。
将 pandas 导入为 pd
df = pd.DataFrame({'name': ['Blue', 'Red', 'Green', 'Purple', 'Yellow'],
'start':[10,23,89,168,21],
'end':[28,25,107,216,40]})
df2 = pd.DataFrame({'name':['W','X','Y','Z'],
'value':[37,176,43,96]})
contained=[]
not_contained = []
def checking(val):
# iterate over df rows
for index, row in df.iterrows():
if val >= row['start'] and val <= row['end']:
# if value is found, append to contained list and return
contained.append(val)
return True
# if value is not found, append it to not_contained list
not_contained.append(val)
# apply function
df2['value'].apply(checking)
print("contained: ",contained)
print("not_contained: ",not_contained)
输出:
contained: [37, 176, 96]
not_contained: [43]
您可以将数据框变成字典,然后查找系列中的每一项。
import pandas as pd
ser = pd.Series(index=list("WXYZ"), data=[37, 176, 43, 96], name="Value")
df = pd.DataFrame(
{
"Start": [10, 23, 89, 168, 21],
"Name": ["Blue", "Red", "Green", "Purple", "Yellow"],
"End": [28, 25, 107, 216, 40],
}
)
df_dict = df.set_index(["Start", "End"])["Name"].to_dict()
ser.apply(
lambda x: next(
(color for bounds, color in df_dict.items() if x in range(*bounds)), None
)
)
这给出了
W Yellow
X Purple
Y None
Z Green
Name: Value, dtype: object
我们的想法是 df_dict
一个以键为边界的字典:
{(10, 28): 'Blue',
(23, 25): 'Red',
(89, 107): 'Green',
(168, 216): 'Purple',
(21, 40): 'Yellow'}
然后循环遍历你的系列,询问每个元素 x
是否在边界之间,使用
`
7 in range(3,5)
# False
7 in range(3,10)
# True
您可以使用 conditional_join from pyjanitor 模拟范围连接,如 SQL -> 请注意,这是在 dev:
# pip install git+https://github.com/pyjanitor-devs/pyjanitor.git
import janitor
import pandas as pd
result = (df2.select_columns('Value')
.conditional_join(df1.select_columns('Start', 'End'),
('Value', 'Start', '>='),
('Value', 'End', '<='),
how = 'left')
)
Value Start End
0 37 21.0 40.0
1 176 168.0 216.0
2 43 NaN NaN
3 96 89.0 107.0
根据 Start
或 End
中的空值过滤到 contained
和 not_contained
:
contained = result.Value[result.Start.notna()].to_list()
not_contained = result.Value[~result.Start.notna()].to_list()
print(contained)
[37, 176, 96]
print(not_contained)
但在我看来,这有点矫枉过正,而且效率不高;你不需要数据框,你只需要一些列表;一种更简单的方法是使用 Pandas' 间隔,再加上 for 循环;对于大型数据帧,我希望这会更有效率:
# create intervalindex
intervals = pd.IntervalIndex.from_arrays(df1.Start, df1.End, closed = 'both')
# We could have used pandas get_indexer,
# however, the intervals are overlapping,
# and get_indexer works only with non-overlapping/unique indexes
contained = []
not_contained = []
for _, value in df2.Value.items():
if intervals.contains(value).any():
contained.append(value)
else:
not_contained.append(value)
print(contained)
[37, 176, 96]
print(not_contained)
[43]
我正在处理一个结构为数据框的值列表,我想将此列表的每个值与另一个数据框(有点像下面的这个)进行比较:
Name Start End
Blue 10 28
Red 23 25
Green 89 107
Purple 168 216
Yellow 21 40
现在假设值列表是这样的:
Name Value
W 37
X 176
Y 43
Z 96
对于“值”列中的每个元素,我想检查该值是否包含在第一个数据帧的每个“开始”-“结束”范围内,并将其添加到列表中(即 contained = []
).在示例中 W (37)
包含在 Yellow
中, X (176)
包含在 Purple
中, Z (96)
包含在 Green
中,而 Y
不匹配所以它将被排除(或添加到另一个列表 not_contained = []
)。
我怎样才能做到这一点?谢谢大家
因为在 sql 中的运算符连接之间没有很好的选择,或者您可以在临时键上交叉连接它们然后过滤结果。
df['key'] = 0
df2['key'] = 0
df.merge(df2, on='key', how='outer').query('Value >= Start & Value <= End').pop('Name_y').tolist()
作为松散的解决方案,您可以遍历一行并匹配值。我已经使用上述方法创建了一个解决方案。 将 pandas 导入为 pd
df = pd.DataFrame({'name': ['Blue', 'Red', 'Green', 'Purple', 'Yellow'],
'start':[10,23,89,168,21],
'end':[28,25,107,216,40]})
df2 = pd.DataFrame({'name':['W','X','Y','Z'],
'value':[37,176,43,96]})
contained=[]
not_contained = []
def checking(val):
# iterate over df rows
for index, row in df.iterrows():
if val >= row['start'] and val <= row['end']:
# if value is found, append to contained list and return
contained.append(val)
return True
# if value is not found, append it to not_contained list
not_contained.append(val)
# apply function
df2['value'].apply(checking)
print("contained: ",contained)
print("not_contained: ",not_contained)
输出:
contained: [37, 176, 96]
not_contained: [43]
您可以将数据框变成字典,然后查找系列中的每一项。
import pandas as pd
ser = pd.Series(index=list("WXYZ"), data=[37, 176, 43, 96], name="Value")
df = pd.DataFrame(
{
"Start": [10, 23, 89, 168, 21],
"Name": ["Blue", "Red", "Green", "Purple", "Yellow"],
"End": [28, 25, 107, 216, 40],
}
)
df_dict = df.set_index(["Start", "End"])["Name"].to_dict()
ser.apply(
lambda x: next(
(color for bounds, color in df_dict.items() if x in range(*bounds)), None
)
)
这给出了
W Yellow
X Purple
Y None
Z Green
Name: Value, dtype: object
我们的想法是 df_dict
一个以键为边界的字典:
{(10, 28): 'Blue',
(23, 25): 'Red',
(89, 107): 'Green',
(168, 216): 'Purple',
(21, 40): 'Yellow'}
然后循环遍历你的系列,询问每个元素 x
是否在边界之间,使用
`
7 in range(3,5)
# False
7 in range(3,10)
# True
您可以使用 conditional_join from pyjanitor 模拟范围连接,如 SQL -> 请注意,这是在 dev:
# pip install git+https://github.com/pyjanitor-devs/pyjanitor.git
import janitor
import pandas as pd
result = (df2.select_columns('Value')
.conditional_join(df1.select_columns('Start', 'End'),
('Value', 'Start', '>='),
('Value', 'End', '<='),
how = 'left')
)
Value Start End
0 37 21.0 40.0
1 176 168.0 216.0
2 43 NaN NaN
3 96 89.0 107.0
根据 Start
或 End
中的空值过滤到 contained
和 not_contained
:
contained = result.Value[result.Start.notna()].to_list()
not_contained = result.Value[~result.Start.notna()].to_list()
print(contained)
[37, 176, 96]
print(not_contained)
但在我看来,这有点矫枉过正,而且效率不高;你不需要数据框,你只需要一些列表;一种更简单的方法是使用 Pandas' 间隔,再加上 for 循环;对于大型数据帧,我希望这会更有效率:
# create intervalindex
intervals = pd.IntervalIndex.from_arrays(df1.Start, df1.End, closed = 'both')
# We could have used pandas get_indexer,
# however, the intervals are overlapping,
# and get_indexer works only with non-overlapping/unique indexes
contained = []
not_contained = []
for _, value in df2.Value.items():
if intervals.contains(value).any():
contained.append(value)
else:
not_contained.append(value)
print(contained)
[37, 176, 96]
print(not_contained)
[43]