Excel有趣函数系列-lookup

一. 前言

注意不是vlookup, 是lookup.

碰到一个这样的问题, 下面动图的函数实现原理:

img

(图源: LOOKUP函数10种经典用法 新手必看)

这个函数并不难理解, 比较大的坑点, 在于其查询内容是需要经过排序的.

注意这里的排序, 这个点类似于pandas中的loc在定位不到数据时(这个功能在pandas上正逐步被废弃), 假如数据不是经过排序, 其返回的内容是异常的.

相关内容见, NoteBook/Pandas-loc的执行逻辑.ipynb at main - Kyouichirou/NoteBook (github.com)

p9m3Go4.png
p9m38wF.png
p9m33eU.png
p9m3lLT.png
p9m3QyV.png

假如不注意到文档的排序要求, 上面出现的各种奇怪内容, 会让人完全摸不着路数.

二. 语法

LOOKUP 函数的使用示例

LOOKUP(lookup_value, lookup_vector, [result_vector])

LOOKUP 函数向量形式语法具有以下参数:

  • lookup_value 必需. LOOKUP 在第一个向量中搜索的值. *Lookup_value* 可以是数字 文本 逻辑值 名称或对值的引用.

  • lookup_vector 必需. 只包含一行或一列的区域. *lookup_vector* 中的值可以是文本 数字或逻辑值.

    重要: lookup_vector* 中的值必须按升序排列*: ..., -2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE; 否则 LOOKUP 可能无法返回正确的值. 文本不区分大小写.

  • result_vector 可选. 只包含一行或一列的区域. *result_vector* 参数必须与 *lookup_vector* 参数大小相同. 其大小必须相同.

2.1 备注

  • 如果 LOOKUP 函数找不到 *lookup_value* 则该函数会与 *lookup_vector* 中小于或等于 *lookup_value* 的最大值进行匹配.
  • 如果 *lookup_value* 小于 *lookup_vector* 中的最小值 则 LOOKUP 会返回 #N/A 错误值.

LOOKUP 的数组形式与 HLOOKUP 和 VLOOKUP 函数非常相似. 区别在于: HLOOKUP 在第一行中搜索 *lookup_value* 的值 VLOOKUP 在第一列中搜索 而 LOOKUP 根据数组维度进行搜索.

  • 如果数组包含宽度比高度大的区域( 列数多于行数) LOOKUP 会在第一行中搜索 *lookup_value* 的值.

  • 如果数组是正方的或者高度大于宽度( 行数多于列数) LOOKUP 会在第一列中进行搜索.

  • 使用 HLOOKUP 和 VLOOKUP 函数 您可以通过索引以向下或遍历的方式搜索 但是 LOOKUP 始终选择行或列中的最后一个值.

    重要: 数组中的值必须按升序排列: ..., -2, -1, 0, 1, 2,

三. 使用

1pcs 1
3个 3
50双 50
5000对 5000

来看看动图中的例子:

=-LOOKUP(1,-LEFT(A2,ROW($1:$5)))

对于excel中, 难以理解的公式(嵌套公式), 只需要拆开就没什么难以理解的.

# -, 这个符号的存在目的在于将数字提取出来, 假如不是数字的内容, 将#value!
=-LEFT(A5,ROW($1:$5))
p9m3sTe.png

row(), 函数只是负责一个任务, 就是传入一个[1,2,3,4,....]的数组给left函数, 返回left函数的数字部分的内容, 前面的负号作用在于区别开数字.

这意味着row(), 只需要拖动从第一行开始, 到最够大的行号即可(从1开始, 任意大小, 主要能够满足字符串最大长度即可).

因为这里的=-LEFT(A5,ROW($1:$5)), 逐个返回数据已经经过排序

# 已经暗含数据是经过排序的了
# 降序排列

-5
-50
-500
-5000
#VALUE!
=-LOOKUP(1,-LEFT(A2,ROW($1:$5)))

# 查找1, 找到1, 找到1, 返回的是1, 找不到1,
# 这里的场景, -LEFT(A2,ROW($1:$5))返回的全是负数
# 这里就涉及到lookup的函数找不到返回值的逻辑问题了

If the LOOKUP function can't find the *lookup_value*, the function matches the largest value in *lookup_vector* that is less than or equal to *lookup_value*.

如果 LOOKUP 函数找不到 *lookup_value* 则该函数会与 *lookup_vector* 中小于或等于 *lookup_value* 的最大值进行匹配.

这个逻辑, 是不是说找不到, 就返回最为接近的?

With the HLOOKUP and VLOOKUP functions, you can index down or across, but LOOKUP always selects the last value in the row or column.

使用 HLOOKUP 和 VLOOKUP 函数 您可以通过索引以向下或遍历的方式搜索 但是 LOOKUP 始终选择行或列中的最后一个值.

这个逻辑, 找不到, 返回最后一个值?

重要: *lookup_vector* 中的值必须按升序排列

假如不是升序, 降序的呢? 返回逻辑是?

微软这个坑货在这里挖的几个坑.

An array is a collection of values in rows and columns (like a table) that you want to search. For example, if you want to search columns A and B, down to row 6. LOOKUP will return the nearest match. To use the array form, your data must be sorted.

为什么在这个位置, your data must be sorted, 不进行加粗

=LOOKUP(5, L2:L5, L2:L5)

=LOOKUP(5, O2:O5, O2:O5)

# 其执行逻辑, 第一个查询值, 但是需要注意不能比查找区域的最小值还小, 否则在找不到值时出现错误.

10	-1		-1	3
3			2
2			3
-1			10

# 降序, 升序两种方式下, 返回的内容的差异

当查找的 5 不在目标时, 降序的列返回的是最后的值, 即 -1, 当满足上述升序这一要求的时候, 返回的是最为接近的数据 , 即 3.

好了, 既然搞明白了, 这个基本逻辑, 那么上面的公式, 就好理解了.

=-LOOKUP(1,-LEFT(A2,ROW($1:$5)))
# 在这里的场景之下, 1, 是不可能出现在-LEFT(A2,ROW($1:$5)
# 即返回的内容一定是最后的行的数据, 即最小的值, 转换为正数, 即最大

3.1 小结

将上面的问题进行更复杂的变化, 数字位置不再是开始的位置, 而可能是前中后都有(小数点也有).

item number
pcs11 11
3个 3
50双 50
5000对 5000
只有20.5只 20.5

实际上, 原理还是很简单

= MAX(IFERROR(ABS(MID(D2,ROW($1:$5),LEN(D2)-IF(ISNUMBER(-RIGHT(D2,1)),0,ROW($1:$5)))), 0))

- ROW()
- ISNUMBER()
- RIGHT
- LEN
- ABS
- IFERROR
- MAX

不用lookup(对数组排序暂时没什么好想法, 读者可自行思考, 能否实现), 使用max函数也能做到 , 其基本思路, 无非如此.

# 第一步判断最后一个文本是否为数字

RIGHT(D2,1) => 得到的是文本 => -RIGHT(D2,1) 暗含转换数字的意思
IF(ISNUMBER(-RIGHT(D2,1))
# 注意, 不然这里的数字判断会出问题

# 假如最后一位不是数字, 即, 逐段截取, 假如是数字, 整个文本返回
# 根据上面的内容对文本进行逐个截取
MID(D2,ROW($1:$5),LEN(D2)-IF(ISNUMBER(-RIGHT(D2,1)),0,ROW($1:$5)))

# mid函数, 文本, 起始位置, 长度

# 还是老套路, abs在这里的目的在于将文本和数字区分开来
ABS(MID(D2,ROW($1:$5),LEN(D2)-IF(ISNUMBER(-RIGHT(D2,1)),0,ROW($1:$5))))

# 将非数字部分剔除掉
IFERROR(ABS(MID(D2,ROW($1:$5),LEN(D2)-IF(ISNUMBER(-RIGHT(D2,1)),0,ROW($1:$5)))), 0)

# 最终取出长度(最大的数字)
MAX(IFERROR(ABS(MID(D2,ROW($1:$5),LEN(D2)-IF(ISNUMBER(-RIGHT(D2,1)),0,ROW($1:$5)))), 0))

excel函数很好玩, 各种函数组合之后貌似能够实现很酷炫(复杂)的功能, 但是完全没有必要, 学习切勿陷入钻研"茴"字的各种写法当中去, 层层嵌套的函数看似很高深莫测而已, 逐层解开, 其变数也多如此.

上面的公式还可以进行精简或者变化.

=MAX(IFERROR( ABS(MID(D6, {1;2;3;4;5},LEN(D6)-IF(ISNUMBER(-RIGHT(D6,1)),0,ROW($1:$5)))), 0))

row($1:$5) => {1;2;3;4;5}, 二者是等价的

# 大括号, => 数组, 即可以表示行, 也能表示列
{1, 5} => 1, 5
{1;5} =>
1
5

excel函数为了方便绝大部分的群体的使用, 在设计上是很费心思, 尽可能做到简洁易用, 没有必要为了复杂而复杂, 上面的内容, 换成正则表达式, 只是基本的入门问题.

过度复杂的嵌套函数不仅运行缓慢, 而且理解起来困难, 徒增维护成本(那怕是自己写的, 当忘记了细节, 想快速定位问题, 维护也是费劲).

瑞士军刀可以砍竹子, 但是瑞士军刀设计目标并不是砍竹子.

p9mNo7V.png

vba中可以引用vbs的正则组件, 在vba中也能使用正则表达式(写个function, 在表格中即可使用).

p9m01I0.png