我在这里遇到了一些问题。我有一个包含大约9,000种有机化合物的电子表格,我正在尝试计算所有这些化合物的分子量。
通常,这很容易:它只是分子式中元素的数量乘以元素的分子量,然后将它们全部加起来。问题是,电子表格的分子式列为字符串。
例如," 乙腈的分子量"在列中列为: C2H3N 。
我想要做的是编写一个扫描该单元格内容的函数,然后说,"好的,每当我遇到文本的内容时,请查看紧随其后的数字直到你点击另一个文本然后停止。然后,取该数字并乘以该特定元素的分子量" (我将在以后处理分子量的总和,因为我觉得这是容易的部分)。
这可能与Excel的内置功能有关,还是我必须使用VBA(我真的没有经验)。非常感谢任何帮助。
答案 0 :(得分:5)
虽然通过一些使用本机Excel函数的非常复杂(和CPU密集型)公式可以轻微地执行您的请求,但VBA 用户定义函数或 UDF 将会非常广泛更合适。我不是化学家,所以请原谅我提供的单个样品的添加,因为它们是从Internet page无耻地偷走的。 TBH,我甚至不确定我的一半术语是否正确。
第1步 - 创建一个分子量表并命名为
您将需要某种形式的交叉引用来从元素的周期符号中检索分子量。这是我拼凑的东西。我将在下面的示例工作簿中提供指向完整数据表的链接。
在名为元素数据的工作表上使用它,转到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 + 中号)。将以下内容粘贴到标题为 Book1 - Module1(Code)的新窗格中。
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函数现在可以在工作簿中使用,就像任何本机Excel函数一样。语法非常简单。
= udf_Molecular_Weight(<单个单元格,复合公式,纯文本> )
对于您的样本化合物(在上面的数据图像中),这将是,
=udf_Molecular_Weight(B2)
......或,
=udf_Molecular_Weight("C2H3N")
有9000多个,我怀疑你会使用前一种方法。必要时填写。虽然这个UDF比使用INDIRECT
和其他本机工作表函数的复杂数组公式更有效,但它们并不神奇。在提交到9000+之前测试几百行的公式,这样您就知道会发生什么。如果您选择使用它们,其他两个UDF的工作方式大致相同。
正如所承诺的,here is a link我为此目的创建的示例.XLSB工作簿供您下载和参考。
Chemical_Compound_Atomic_Weights.xlsb
该链接将保持活跃状态一段时间。如果我将其位置更改为更长久的存储空间,我将在此处调整链接。
简要说明:
通过'变量声明',我猜你实际上是指'变量赋值'。我倾向于编写相当紧密的代码,并且通过将变量的归零用冒号堆叠,我已经将其他人将最多4个代码行放入单行中的内容。我转过来了,
sTMP = sCMPND
dAWEIGHT = 0
sSB = "0"
sEL = vbNullString
......进入这个,
sTMP = sCMPND: dAWEIGHT = 0: sSB = "0": sEL = vbNullString
IT行业有史以来最严重的错误之一是会计师决定向程序员支付他们编写的每一行代码。
在重新进入循环之前需要重置变量,但这是一项平凡的任务,所以我只需将所有四个赋值塞入一行。
两个Do While ... Loop
抓取通过字符传递给函数字符的字符串。内循环专门处理数字。每次通过循环都会截断左边的字符串,将其缩短一个或多个字符,并将这些字符收集为元素的符号或与其在有机化合物中使用相关的数字。最终没有任何东西可以截断(长度= 0),这就是CBool(Len(sTMP))
变为 False 并且循环结束的地方。内循环的执行方式大致相同,但收集数字直到达不到长度或字母字符。在收集了元素(和可能的数字修饰符)之后,化合物中该元素的分子量用分子量表的VLOOKUP
计算并加到越来越多的数字中。当收集了所有元素及其相关数字并将其添加到总计中时,将返回该总数作为该函数的结果。
答案 1 :(得分:3)
@Jeeped有一个很棒的VBA解决方案。我在 How to count up elements in excel 上发布了针对相关问题的非VBA解决方案。它很容易扩展到这个问题。
将每个元素放在一个单独的列中,其原子质量高于它。
该公式将计算分子中每个原子的重量:
=B$1*
MAX(IFERROR(IF(FIND(B$2&ROW($2:$100),$A3),ROW($2:$100),0),0),
IFERROR(IF(FIND(B$2&CHAR(ROW($66:$91)),$A3&"Z"),1,0),0)
)
以数组公式输入: Ctrl + Shift + 输入。
总分子量是重量的总和。
示例:强>