我有一个相当大的数据集,我需要将多个条目合并为一个值。我的数据集包含有关两个数据集的组合的数据,每个数据集都使用自己的ID和密钥。
我想到使用像这样的Sumproduct()
函数:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O={20;21;22;23;40});'Raw data'!S:S)
Landgebruik!A2
拥有第一个数据集的ID,我需要将其聚合到第二个数据集。
'Raw data'!O:O
包含第二个数据集的ID。在上述情况下,当第二个ID的值为以下任何一个值时,我需要对'Raw data'!S:S
中的区域求和:{20;21;22;23;40}
。 (或逻辑)该列仅包含整数值。
还有其他解决方法,然后为数组中的所有值复制--('Raw data'!O:O=20)
吗?
编辑:
我现在使用了变通方法,即:=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O=20)+('Raw data'!O:O=20)+('Raw data'!O:O=21)+('Raw data'!O:O=22)+('Raw data'!O:O=23)+('Raw data'!O:O=40);'Raw data'!S:S)
。但是我觉得应该有一种更优雅的方法。
答案 0 :(得分:6)
即使这已经做过数百次了,但也许微软会调换公式之类的东西。
我偏爱Jerry和Me所建议的方法,因为它们很简单又简洁,但是您付出了沉重的性能成本。
汤姆的公式对我来说看起来很丑,但到目前为止最快,比我最初的例子快4倍。我们能够将{}与Tom的公式合并,但是要使其正常工作,我们必须将sumifs函数与sum函数包装在一起。这大大降低了配方的速度,但使其更漂亮。
z32a7ul也有很好的解决方案。我真的很喜欢-的用法,并学会了如何使用| s搜索文本,并且仅搜索该文本。乍一看,我认为它不适用于2323等数字,但确实可以。
模拟示例如下:
A1:A5000中充满了LandgeBruik,
B1:B5000充满了40个
C1:5000中充满了1。
结果:
=SUMPRODUCT((A1:A5000="LandgeBruik")*(B1:B5000={20,21,22,23,40})*C1:C5000)
经过了19.186031秒
59,818,073滴答声
{=SUM(IF(A1:A5000="Landgebruik",1,0)*IF(B1:B5000={20,21,22,23,40},1,0)*C1:C5000)}
经过26.124411秒
81,450,506个滴答声
{=SUM((A1:A5000=""Landgebruik"")*(B1:B5000={20,21,22,23,40})*C1:C5000)}
经过21.111835秒
65,822,330滴答
"=SUMIFS(C1:C5000,B1:B5000,"">=20"",B1:B5000,""<=23"",A1:A5000,""=Landgebruik"")+SUMIFS(C1:C5000,B1:B5000,""=40"",A1:A5000,""=Landgebruik"")"
经过6.732804秒
20,991,490滴答
"=SUM(SUMIFS(C1:C5000,A1:A5000,"Landgebruik",B1:B5000,{21,22,23,24,40}))"
已逝去16.954528秒
52,860,709滴答声
"=SUMPRODUCT(--(A1:A5000=""Landgebruik""),--NOT(ISERROR(FIND(""|""&B1:B5000&""|"",""|20|21|22|23|40|""))),C1:C5000)"
经过11.822379秒
36,859,729个滴答声
方便的类 TimerWin64 用于计时这些时间:
显式选项
Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LongInteger) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LongInteger) As Long
Private Type LongInteger
First32Bits As Long
Second32Bits As Long
End Type
Private Type TimerAttributes
CounterInitial As Double
CounterNow As Double
PerformanceFrequency As Double
End Type
Private Const MaxValue_32Bits = 4294967296#
Private this As TimerAttributes
Private Sub Class_Initialize()
PerformanceFrequencyLet
End Sub
Private Sub PerformanceFrequencyLet()
Dim TempFrequency As LongInteger
QueryPerformanceFrequency TempFrequency
this.PerformanceFrequency = ParseLongInteger(TempFrequency)
End Sub
Public Sub CounterInitialLet()
Dim TempCounterIntital As LongInteger
QueryPerformanceCounter TempCounterIntital
this.CounterInitial = ParseLongInteger(TempCounterIntital)
End Sub
Public Sub PrintTimeElapsed()
CounterNowLet
If CounterInitalIsSet = True Then
Dim TimeElapsed As Double
TimeElapsed = (this.CounterNow - this.CounterInitial) / this.PerformanceFrequency
Debug.Print Format(TimeElapsed, "0.000000"); " seconds elapsed "
Dim TicksElapsed As Double
TicksElapsed = (this.CounterNow - this.CounterInitial)
Debug.Print Format(TicksElapsed, "#,##0"); " ticks"
End If
End Sub
Private Function CounterNowLet()
Dim TempTimeNow As LongInteger
QueryPerformanceCounter TempTimeNow
this.CounterNow = ParseLongInteger(TempTimeNow)
End Function
Private Function CounterInitalIsSet() As Boolean
If this.CounterInitial = 0 Then
MsgBox "Counter Initial Not Set"
CounterInitalIsSet = False
Else
CounterInitalIsSet = True
End If
End Function
Private Function ParseLongInteger(ByRef LongInteger As LongInteger) As Double
Dim First32Bits As Double
First32Bits = LongInteger.First32Bits
Dim Second32Bits As Double
Second32Bits = LongInteger.Second32Bits
If First32Bits < 0 Then First32Bits = First32Bits + MaxValue_32Bits
If Second32Bits < 0 Then Second32Bits = First32Bits + MaxValue_32Bits
ParseLongInteger = First32Bits + (MaxValue_32Bits * Second32Bits)
End Function
以下是用于运行测试的代码:
Option Explicit
Sub testFunctions()
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
.DisplayStatusBar = False
.EnableEvents = False
End With
Dim ws As Worksheet
Set ws = Sheets("Test")
Dim Index As Long
Dim Timer As TimerWin64
Set Timer = New TimerWin64
Timer.CounterInitialLet
For Index = 1 To 5000
Dim rngAddress As String
rngAddress = "D" & Index
ws.Range(rngAddress).Formula = "=SUMPRODUCT(--(A1:A5000=""Landgebruik""),--NOT(ISERROR(FIND(""|""&B1:B5000&""|"",""|20|21|22|23|40|""))),C1:C5000)"
Next Index
Timer.PrintTimeElapsed
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.DisplayStatusBar = True
.EnableEvents = True
End With
End Sub
答案 1 :(得分:6)
您可以为此使用文本搜索:
--NOT(ISERROR(FIND('Raw data'!O:O,"2021222340")))
但是您必须小心,不要在较长的ID中错误地找到较短的ID,例如如果您要在ID {123,456,789}中进行搜索,则ID中不包含12。因此,像上面这样的简单文本搜索将无法工作。您需要使用分隔符来分隔ID字符串。通常,我将管道字符用于此目的,因为我不记得它在Excel文件的原始文本中出现的任何情况,并且因为它使公式易于阅读:
--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|")))
示例:
“原始数据”!O:O为20 => | 21 |在| 20 | 21 | 22 | 23 | 40 |
中找到“原始数据”!O:O为2 => | 2 |在| 20 | 21 | 22 | 23 | 40 |
中找不到(如果您的ID可能包含竖线字符,则可以使用CHR(1),它是SOH的一个很长的被遗忘的ASCII码,表示标头的开头;当然,它的可读性较低。)
整个公式:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|"))),'Raw data'!S:S)
(对不起,我的Excel使用,而不是;)
答案 2 :(得分:5)
您可以按照注释中所述将其拆分为两个SUMIFS。如果所有值都是整数,则将“原始数据”!O:O与20,21,22和23进行比较与测试> = 20和<= 23相同。值40必须单独完成。
=SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,">="&20,'Raw Data'!O:O,"<="&23)
+SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,40)
在我的区域设置
或
=SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;">="&20;'Raw Data'!O:O;"<="&23)
+SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;40)
在您的语言环境中。
这仅在多个条件为连续整数时有效。
速度注意事项
SUMIFS被认为比sumproduct快大约五倍,因此对于大型数据集as demonstrated here
可能是首选您可能会说,(有效)@ BrakNicku的SUM中的五个SUMIFS的更笼统的建议应该与一个SUMPRODUCT差不多快,但是SUM(SUMIFS)仍然会获胜,因为SUMIFS之类的公式可以处理全列引用比数组公式更有效。
答案 3 :(得分:5)
您可以对当前公式进行一些小的更改;将;
更改为*
(在特定情况下也不需要--
)
=SUMPRODUCT(('Raw data'!C:C=Landgebruik!A2)*('Raw data'!O:O={20;21;22;23;40})*'Raw data'!S:S)
那应该可行。
当您将单独的参数提供给SUMPRODUCT
时,每个参数的大小必须相同。但是,当您像这样乘以它们时,它将强制求值,并且数组会扩展。
例如,如果采用两个数组5x1和1x5,则会得到一个5x5的结果数组:
答案 4 :(得分:4)
在向OP提出一些澄清要求后,我想对此问题做一个解答,因为英语不是我的主要语言,并且我认为我误会了一些东西。
所以,我为模拟情况所做的工作,用两张纸制成了一个新的工作簿。
一个工作表名为import threading
from queue import Queue
import requests
hist_lock = threading.Lock()
def opthist_job(worker,d_thread):
headers = {
'Pragma': 'no-cache',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'Accept': '*/*',
'Referer': 'https://www.nseindia.com/products/content/derivatives/equities/historical_fo.htm',
'X-Requested-With': 'XMLHttpRequest',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
}
params = d_threading[0] # This is where I need to get the value of key
opthistdf = requests.get('https://www.nseindia.com/products/dynaContent/common/productsSymbolMapping.jsp', headers=headers, params=params)
with hist_lock: # I am not sure if this is required in this instance.
#### Some more functions ####
def threader():
while True:
worker = q.get()
opthist_job(worker)
q.task_done()
q = Queue()
for th in range(len(d_threading.keys())):
t=threading.Thread(target=threader)
t.daemon = True
t.start()
,并在version: 2.1
orbs:
aws-s3: circleci/aws-s3@1.0.3
中得到了一个值,我这样做了:
第二张纸名为Landgebruik
。我隐藏了一些列以仅使用C,O和S列。在SI列中,输入的值仅等于1。在OI列中,随机值等于A2
;在CI列中,随机值是A或B。看起来像这样(请注意,我隐藏了一些列):
问题想对S列中的值求和,但仅当O列等于20或21或22或23 o 40且C列等于Raw data
时(在我的测试中,其中的值是字母{20,21,22,23,40}
)
我们可以使用数组公式来过滤S列中的数据,然后在过滤后对符合要求的值求和。在我的测试中,正确的结果应该是8,因为S列中只有8个值满足C和O列的要求。在图像中,右行以黄色突出显示。
OP已经做到了这一点,但想知道是否存在一个更短/更优雅的公式。
我发现最短的公式是这样的:
Landgebruik!A2
这是一个数组公式,因此必须按插入 CTRL + SHIFT + ENTER 否则将无效!
工作原理:
第一个A
接受S列中的所有值,并忽略所有与O列中的等效值不等于20或21或22或23或40的值。第二个=SUM(IF($O$2:$O$28={20;21;22;23;40};IF($C$2:$C$28=Landgebruik!$A$2;$S$2:$S$28)))
接受该新数组,并忽略所有值其中C列中的等效项不等于IF
。最终数组由函数IF
我已经尽力解释了。我希望您能适应您的需求。
答案 5 :(得分:4)
如果您对性能(计算速度)感兴趣并且不害怕矩阵计算,则可以使用MMULT:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),MMULT(--('Raw data'!O:O={20,21,22,23,24}),TRANSPOSE({1,1,1,1,1})),'Raw data'!S:S)
说明:
首先,创建一个1048576×5矩阵,如果'原始数据'中的ID与O:O的第i行相同,则第i行和第j列的值为1。枚举{20,21,22,23,24}中的第j个值,否则为0。
第二,将其乘以1s(因为{20,21,22,23,24}包含五个元素,所以为5 1s),这表示您接受所有五个值。
第三,从上面得到一个向量,如果ID在可接受的值中,则第i个元素为1,否则为0,然后将该向量放在SUMPRODUCT中的其他向量旁边。
(对不起,我的Excel使用','而不是';'。如果要缩短公式,可以写{1; 1; 1; 1; 1}而不是TRANSPOSE({1,1,1 ,1,1})。但是您必须找出Excel用什么代替“;”来分隔行,最有可能是“。”。)
注意:如果您引用的是实际包含值而不是整个列的范围,例如,可能会提高计算速度。原始数据!C1:C123而不是原始数据!C:C。
如果使用Shift + Space Ctrl ++在已包含的最后一行上方插入新行,则公式中的引用将自动更新。另外,您可以将“名称”与特殊公式配合使用,以通过确定最后一个非空单元格来扩大所引用的范围。
更新
我进行了一些测量,以比较这些方法的效率。我使用10000行的随机数据,并对每个公式重新计算了1000次。您可以在第二列中看到经过的时间。
在运行此VBA代码以测量时间时,我注释了其他公式:
Public Sub MeasureCalculationTime()
Dim datStart As Date: datStart = Now
Dim i As Long: For i = 1 To 1000
Application.Calculate
Next i
Dim datFinish As Date: datFinish = Now
Dim dblSeconds As Double: dblSeconds = (datFinish - datStart) * 24 * 60 * 60
Debug.Print "Calculation finished at " & datFinish; " took " & dblSeconds & " seconds"
End Sub
在这种情况下,MMULT并不是最快的。
但是,我想指出这是最灵活的,因为
您可以将其与开关一起使用:引用单元格区域而不是{1,1,1,1,1},您将可以非常快速地在选择中包括/排除ID。就像您放入A1:A5 {20,21,22,23,24}并紧随其后,放入B1:B5 {1,1,1,1,1}。如果要排除21,则将B2重写为0,如果要包含B2,则将其写回1。
您可能使用更复杂的条件,在这里您必须比较多个级别。喜欢:
= SUMPRODUCT(MMULT(-(CarId = CarOwner),-(CarOwner = ListOfJobs),-(ListOfJobs = JobsByDepartment),-(DepartmentIncludedInSelection = 1)),燃料消耗)
注意:上一行只是伪代码,MMULT只有两个参数。
答案 6 :(得分:-1)
这可能有效:
={SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--IFERROR(MATCH('Raw data'!O:O;{20;21;22;23;40};0)>0;0);'Raw data'!S:S)}
这需要作为数组公式输入。