一、背景
给定一个维表,如下:
区域 编码范围
N0 1000-1935, 2000-2079, 2085-2107
GF 2250-2263
WG 2500-2507, 2515-2532
NC 2282-2310
BR 3350, 3353-3356
V2 2648, 2715, 2717-2719
V3 3689-3690
Q5 4225
每一个区域编码,都会包含多个不同、不一定连续的编码,如编码范围列所示,要求给定一个编码值,求所对应的区域。
二、思路
1.思路一
将编码范围进行拆分,构造出每一个编码与区域的键值对,生成一个字典,再进行值的查找。
实现最终效果如下:
0 N0 1000
1 N0 1001
2 N0 1002
3 N0 1003
4 N0 1004
5 N0 1005
......
9158 NT2 901
9159 NT2 902
9160 NT2 903
9161 NT2 904
9162 NT2 905
再通过列值的查找,即可返回对应的区域。
此方法计算量较大,需要将每一个编码跨度的每一个取值均生成出来。
2.思路二
后续优化的时候,考虑其实无须将每一个编码都生成 duck不必!
,只需要判断编码是否连续。
连续的编码,取最小值(下限)、最大值(上限)构造两列。
非连续编码,令最大值=最小值。
如下所示:
区域 Min_value Max_value
0 N0 1000 1935
1 N0 2000 2079
2 N0 2085 2107
3 GF 2250 2263
4 WG 2500 2507
5 WG 2515 2532
6 NC 2282 2310
7 BR 3350 3350
8 BR 3353 3356
9 V2 2648 2648
10 V2 2715 2715
11 V2 2717 2719
12 V3 3689 3690
13 Q5 4225 4225
最后通过判断:Min_value <= 编码 <= Max_value
即可。
3.思路三
类似于思路二,只是中间直接用 reset_index() + stack()
直接堆叠实现。
三、实操
1.思路一实操
# 追加 构造字典方式
import os
import pandas as pd
os.chdir(r'C:\Users\111\Desktop')
# 读取原始表
df = pd.read_excel(r'df.xlsx')
result = []
# 按行循环 效率低
for i in range(len(df)):
# 每一行
print(i)
# 判断是否包括逗号
if ',' not in str(df.loc[i, '编码范围']):
# 判断是否包括横线
if '-' not in str(df.loc[i, '编码范围']):
# 不包括的直接追加结果表
result.append([df.loc[i, '区域'], df.loc[i, '编码范围']])
else:
# 没逗号有横线的 切分为最小值,最大值,生成追加结果表
a, b = [int(x) for x in df.loc[i, '编码范围'].split('-')]
for x in range(a, b+1):
# print(x)
result.append([df.loc[i, '区域'], x])
# 有逗号
else:
for y in df.loc[i, '编码范围'].split(','):
# print(y)
# 有逗号 没横线 直接追加
if '-' not in y:
result.append([df.loc[i, '区域'], y])
# print(y)
else:
# 有逗号有横线
c, d = [int(x) for x in y.split('-')]
# print(c, d)
for x in range(c, d+1):
# print(x)
result.append([df.loc[i, '区域'], x])
result = pd.DataFrame(result, columns=['区域', '编码范围'])
result['编码范围'] = result['编码范围'].astype(int)
result.to_excel(r'result.xlsx')
result.sort_values(by='编码范围', inplace=True)
result.reset_index(drop=True, inplace=True)
result
'''
区域 编码范围
0 N0 1000
1 N0 1001
2 N0 1002
3 N0 1003
4 N0 1004
.. ...
1116 BR 3355
1117 BR 3356
1118 V3 3689
1119 V3 3690
1120 Q5 4225
[1121 rows x 2 columns]
'''
2.思路二实操
- 构造编码范围上下限
import os
import pandas as pd
os.chdir(r'C:\Users\111\Desktop')
# 读取数据
df = pd.read_excel(r'df.xlsx')
# 如果包含逗号则进行分列
df['编码范围'] = df['编码范围'].apply(lambda x: x.split(',') if ',' in str(x) else x)
# 炸裂 列转多行
df = df.explode('编码范围').reset_index(drop=True)
# 去除头尾多余的空格
df['编码范围'] = df['编码范围'].apply(lambda x: str(x).strip())
# 按横线切割再展开 构造最小值、最大值列
df[['Min_value', 'Max_value']] = df['编码范围'].str.split('-', expand=True).rename(columns={0:'Min_value', 1:'Max_value'})
# 最大值列为空填充为最小值
df.loc[df['Max_value'].isnull(), 'Max_value'] = df.loc[df['Max_value'].isnull(), 'Min_value']
# 转换为数值
df['Min_value'] = df['Min_value'].astype(int)
df['Max_value'] = df['Max_value'].astype(int)
# 查看类型
df.dtypes
'''
区域 object
编码范围 object
Min_value int32
Max_value int32
dtype: object
'''
#### 输出 ####
'''
区域 编码范围 Min_value Max_value
0 N0 1000-1935 1000 1935
1 N0 2000-2079 2000 2079
2 N0 2085-2107 2085 2107
3 GF 2250-2263 2250 2263
4 WG 2500-2507 2500 2507
5 WG 2515-2532 2515 2532
6 NC 2282-2310 2282 2310
7 BR 3350 3350 3350
8 BR 3353-3356 3353 3356
9 V2 2648 2648 2648
10 V2 2715 2715 2715
11 V2 2717-2719 2717 2719
12 V3 3689-3690 3689 3690
13 Q5 4225 4225 4225
'''
- 构造函数判断取值
def find_email(df, n):
'''
df 展开的维表
n 待查找的编码
'''
is_or_not = (df['Min_value'] <= n) & (df['Max_value'] >= n)
return df.loc[is_or_not, '区域'].values[0]
result = find_email(df, 2718)
print(f'所查找的邮编归属区域为:{result}')
# 所查找的邮编归属区域为:V2
3.思路三实操
# 直接堆叠
df = pd.read_excel(r'df.xlsx')
df.set_index('区域', inplace=True)
df['编码范围'] = df['编码范围'].astype(str)
df = df['编码范围'].str.split(',', expand=True)
df = df.stack().reset_index()[['区域', 0]].rename(columns={0: 'Code_min_max'})
df.set_index('区域', inplace=True)
df = df['Code_min_max'].str.split('-', expand=True).rename(columns={0:'Min', 1:'Max'}).reset_index()
df.loc[df['Max'].isnull(), 'Max'] = df.loc[df['Max'].isnull(), 'Min']
x = 2718
y = df.loc[(df['Min'].astype(int) < x) & ( df['Max'].astype(int) > x), '区域'].values[0]