我在这里遇到了一些问题。我有一个包含大约 9,000 种有机化合物的电子表格,我正在尝试计算所有这些化合物的分子量。
通常,这很容易:它只是分子式中元素的数量乘以元素的分子量,然后将它们全部加起来。问题是,电子表格将分子式列为字符串。
例如,“ 乙腈 ”的分子量在列中列为: C2H3N .
我想做的是编写一个函数来扫描该单元格的内容并说:“好的,每次我遇到文本时,请查看紧随其后的数字,直到您点击另一个文本然后停止。然后,采取这个数字乘以那个特定元素的分子量”(我稍后会处理分子量的总和,因为我觉得这是最容易的部分)。
这可能与Excel的内置函数有关,还是我必须使用VBA(我真的没有经验)。在这里的任何帮助将不胜感激。
最佳答案
虽然您的请求可以通过一些非常复杂(和 CPU 密集型)公式仅使用原生 Excel 函数、VBA 用户定义函数或 来实现。 UDF 会更合适。我不是化学家,所以请原谅我提供的对您的单个 sample 的添加,因为它们是从 Internet page 无耻地偷走的。 . TBH,我什至不确定我是否有一半的术语是正确的。
第 1 步 - 创建一个分子量表并将其命名为
您将需要某种形式的交叉引用来从元素的周期符号中检索分子量。这是我拼凑的。我将在下面的示例工作簿中提供指向完整数据表的链接。
在名为 Element Data 的工作表上,转到 Formulas ► Defined Names ► Name Manger
并给交叉引用矩阵一个定义的名称。
在这里,我使用了一个公式 (=OFFSET('Element Data'!$A$1,0,0,COUNTA( 'Element Data'!$A:$A),6)
) 来定义范围,但数据的大小是相当静态的,因此单元格范围引用应该绰绰有余。
第 2 步 - 添加用户定义函数的代码
点击 Alt+F11,当 VBE 打开时,立即使用下拉菜单到 Insert ► Module
(Alt+I+M)。将以下内容粘贴到标题为 Book1 - Module1 (Code) 的新 Pane 中。
Public Function udf_Molecular_Weight(sCMPND As String) As Double
Dim sTMP As String, i As Long, sEL As String, sSB As String
Dim dAW As Double, dAWEIGHT As Double, dSUB As Long
sTMP = sCMPND: dAWEIGHT = 0: sSB = "0": sEL = vbNullString
Do While CBool(Len(sTMP))
sSB = "0": sEL = vbNullString
If Asc(Mid(sTMP, Application.Min(2, Len(sTMP)), 1)) > 96 Then
sEL = Left(sTMP, 2)
Else
sEL = Left(sTMP, 1)
End If
sTMP = Right(sTMP, Len(sTMP) - Len(sEL))
Do While IsNumeric(Left(sTMP, 1))
sSB = sSB & Int(Left(sTMP, 1))
sTMP = Right(sTMP, Len(sTMP) - 1)
Loop
'Debug.Print sEL & ":" & (Int(sSB) - (Not CBool(Int(sSB))))
dAWEIGHT = dAWEIGHT + Application.VLookup(sEL, ThisWorkbook.Names("tblPeriodic").RefersToRange, 6, False) * (Int(sSB) - (Not CBool(Int(sSB))))
Loop
udf_Molecular_Weight = dAWEIGHT
End Function
Public Function udf_Styled_Formula_Alt(sCMPND As String) As String
Dim sb As Long, sCOMPOUND As String
sCOMPOUND = sCMPND
For sb = 0 To 9
sCOMPOUND = Replace(sCOMPOUND, sb, ChrW(8320 + sb))
Next sb
udf_Styled_Formula_Alt = sCOMPOUND
End Function
Public Function udf_Unstyled_Formula_Alt(sCMPND As String) As String
Dim sb As Long, sCOMPOUND As String
sCOMPOUND = sCMPND
For sb = 0 To 9
sCOMPOUND = Replace(sCOMPOUND, ChrW(8320 + sb), sb)
Next sb
udf_Unstyled_Formula_Alt = sCOMPOUND
End Function
只有第一个与您发布的问题有关。后两者使用 Unicode 下标字符对化合物的化学式进行风格化,并反转该过程。
完成粘贴后,点击 Alt+Q 返回工作表。这些 UDF 函数现在可以在您的工作簿中使用,就像任何 native Excel 函数一样。语法尽可能简单。
=udf_Molecular_Weight(<纯文本中具有复合公式的单个单元格>)
对于您的 sample 化合物(在上面的数据图像中),这将是,
=udf_Molecular_Weight(B2)
... 或者,
=udf_Molecular_Weight("C2H3N")
有了其中的 9000 多个,我怀疑您会使用前一种方法。根据需要填写。虽然这个 UDF 比使用
INDIRECT
的卷积数组公式要高效得多。和其他 native 工作表功能,它们并不神奇。在提交到 9000+ 之前,在几百行上测试公式,这样你就知道会发生什么。如果您选择使用其他两个 UDF,它们的工作方式大致相同。简要说明:
通过“变量声明”,我猜您实际上是指“变量分配”。我倾向于编写相当紧凑的代码,并且通过将变量的归零与冒号堆叠在一起,我将其他人最多可以放入 4 行代码行中的内容放在一行中。我转这个,
sTMP = sCMPND
dAWEIGHT = 0
sSB = "0"
sEL = vbNullString
...进入这个,
sTMP = sCMPND: dAWEIGHT = 0: sSB = "0": sEL = vbNullString
在重新进入循环之前需要重置变量,但这是一项平凡的任务,所以我只是将所有四个分配都塞进一行。
两个
Do While ... Loop
逐个字符地遍历传递给函数的字符串。内部循环专门处理数字。每次通过循环都会从左侧截断字符串,将其缩短一个或多个字符,并将这些字符收集为元素的符号或与其在有机化合物中的使用相关的数字。最终没有任何东西可以截断(长度=0),这就是CBool(Len(sTMP))
。变为 False 并且循环结束。内部循环的执行方式大致相同,但会收集数字直到它没有长度或字母字符。收集到元素(和可能的数字修饰符)后,化合物中该元素的分子量使用 VLOOKUP
计算。反对分子量表并添加到越来越多的数字。当所有元素及其相关数量已收集并添加到总计中时,总计将作为函数的结果返回。
关于excel - 使用 Excel 计算分子量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27050976/