awk中的关联数组具有挑战性的内存限制
这与我最近在
Awk code with associative arrays — array doesn’t seem populated,but no error和
optimizing loop,passing parameters from external file,naming array arguments within awk的帖子有关
我在这里的基本问题只是从详细的古代档案金融市场数据,#transactions,#shares,value,BY DATE,FIRM-ID,EXCHANGE等的每日聚合进行计算.学习在awk中使用关联数组,并且很高兴能够在11分钟的时钟时间内处理1.29亿行.直到我喝完咖啡之前. 变得更加雄心勃勃,从2个阵列下标转移到4个,现在我无法一次处理超过6500行. 获取表单的错误消息:
在一些运行中,机器告诉我它缺少52 KB的内存.我对Win-7和8MB RAM的std配置有所了解. (经济学家通过培训,而不是计算机科学家.)我意识到,从2到4阵列使计算机上的问题在计算上变得更加复杂,但是有一些人可以做些什么来改善内存管理至少一点点.我试过关闭我正在做的其他事情.该错误始终只与内存有关,从不与磁盘空间或其他任何内容有关. 示例INPUT: 49290,C198962542782200306,6/30/2003,433581,F5811773991200306,S5405611832200306,B5086397478200306,NESTLE INDIA LTD.,INE239A01016,6/27/2003,1,E9035083824200306,REG_DL_STLD_02,591.13,5655,3342840.15,REG_DL_INSTR_EQ,REG_DL_DLAY_P,DL_RPT_TYPE_N,DL_AMDMNT_DEL_00 49291,433563,F6292896459200306,S6344227311200306,B6110521493200306,GRASIM INDUSTRIES LTD.,INE047A01013,495.33,3700,1832721,DL_AMDMNT_DEL_00 49292,433681,F6513202607200306,S1724027402200306,B6372023178200306,HDFC BANK LTD,INE040A01018,6/26/2003,E745964372424200306,242,2600,629200,REG_DL_DLAY_D,DL_AMDMNT_DEL_00 49293,C7885768925200306,48128,F4406661052200306,S7376401565200306,B4576522576200306,Maruti Udyog Limited,INE585B01010,6/28/2003,3,E912851176274200306,REG_DL_STLD_04,125,44600,5575000,DL_AMDMNT_DEL_00 49294,48129,F4500260787200306,S1312094035200306,4,445600,55700000,DL_AMDMNT_DEL_00 49295,48130,F6425024637200306,S2872499118200306,48000,6000000,REG_DL_INSTR_EU,DL_AMDMNT_DEL_00 码 BEGIN { FS = "," } # For each array subscript variable -- DATE ($10),firm_ISIN ($9),EXCHANGE ($12),and FII_ID ($5),after checking for type = EQ,set up counts for each value,and number of unique values. ( $17~/_EQ&;/ ) { if (date[$10]++ == 0) date_list[d++] = $10; if (isin[$9]++ == 0) isin_list[i++] = $9; if (exch[$12]++ == 0) exch_list[e++] = $12; if (fii[$5]++ == 0) fii_list[f++] = $5; } # For cash-in,buy (B),or cash-out,sell (S) count NR = no of records,SH = no of shares,RV = rupee-value. (( $17~/_EQ&;/ ) && ( $11~/1|2|3|5|9|1[24]/ )) {{ ++BNR[$10,$9,$12,$5]} {BSH[$10,$5] += $15} {BRV[$10,$5] += $16} } (( $17~/_EQ&;/ ) && ( $11~/4|1[13]/ )) {{ ++SNR[$10,$5]} {SSH[$10,$5] += $15} {SRV[$10,$5] += $16} } END { { print NR,"records processed."} { print " " } { printf("%-11st%-13st%-20st%-19st%-7st%-7st%-14st%-14st%-18st%-18sn", "DATE","ISIN","EXCH","FII","BNR","SNR","BSH","SSH","BRV","SRV") } { for (u = 0; u < d; u++) { for (v = 0; v < i; v++) { for (w = 0; w < e; w++) { for (x = 0; x < f; x++) #check first below for records with zeroes,don't print them { if (BNR[date_list[u],isin_list[v],exch_list[w],fii_list[x]] + SNR[date_list[u],fii_list[x]] > 0) { BR = BNR[date_list[u],fii_list[x]] SR = SNR[date_list[u],fii_list[x]] BS = BSH[date_list[u],fii_list[x]] BV = BRV[date_list[u],fii_list[x]] SS = SSH[date_list[u],fii_list[x]] SV = SRV[date_list[u],fii_list[x]] { printf("%-11st%13st%20st%19st%7dt%7dt%14dt%14dt%18.2ft%18.2fn", date_list[u],fii_list[x],BR,SR,BS,SS,BV,SV) } } } } } } } } 预期产出 6 records processed. DATE ISIN EXCH FII BNR SNR BSH SSH BRV SRV 6/27/2003 INE239A01016 E9035083824200306 F5811773991200306 1 0 5655 0 3342840.15 0.00 6/27/2003 INE047A01013 E9035083824200306 F6292896459200306 1 0 3700 0 1832721.00 0.00 6/26/2003 INE040A01018 E745964372424200306 F6513202607200306 1 0 2600 0 629200.00 0.00 6/28/2003 INE585B01010 E912851176274200306 F4406661052200306 1 0 44600 0 5575000.00 0.00 6/28/2003 INE585B01010 E912851176274200306 F4500260787200306 0 1 0 445600 0.00 55700000.00 在这种情况下,当输入记录的数量超过6500时,我最终会遇到内存问题.总共有大约700万条记录. 对于2数组下标问题,尽管在不同的数据集上,在同一台机器上使用相同的GNU-AWK在11分钟的时钟时间内处理了1.29亿行,请参阅optimizing loop,naming array arguments within awk 问题:awk在内存管理方面是不是很聪明,但是其他一些更现代的工具(比如说SQL)会用相同的内存资源来完成这项任务吗?或者这只是关联数组的一个特征,我发现它让我能够避免对数据,许多循环和SORT程序进行多次传递,但是它可能很好地工作到2个数组下标,然后面对指数内存资源成本那? 后记:超级详细的几乎防止白痴的教程以及Ed Morton在下面的评论中提供的代码产生了巨大的差异,尤其是他的GAWK脚本tst.awk.他告诉我(a)智能地使用SUBSEP(b)处理不必要的循环,这在这个问题中至关重要,这个问题往往具有非常稀疏的数组,具有各种AWK结构.与我的旧代码的性能相比(在一台机器上只接受了多达6500行输入,另一台甚至无法达到这一点),Ed Morton的tst.awk的性能可以从下表中看出: **filename start end min in ln out lines 2008_1 12:08:40 AM 12:27:18 AM 0:18 391438 301160 2008_2 12:27:18 AM 12:52:04 AM 0:24 402016 314177 2009_1 12:52:05 AM 1:05:15 AM 0:13 302081 238204 2009_2 1:05:15 AM 1:22:15 AM 0:17 360072 276768 2010_1 "slept" 507496 397533 2010_2 3:10:26 AM 3:10:50 AM 0:00 76200 58228 2010_3 3:10:50 AM 3:11:18 AM 0:00 80988 61725 2010_4 3:11:18 AM 3:11:47 AM 0:00 86923 65885 2010_5 3:11:47 AM 3:12:15 AM 0:00 80670 63059** 简单地通过在执行tst.awk之前和之后的行上使用%time%来获得时间,所有这些都放在一个简单的批处理脚本中,“min”是所花费的时钟时间(默认情况下EXCEL执行的任何舍入),“in ln” “out line”分别是输入和输出的行.从处理我们拥有的整个数据,从2003年1月到2014年1月,我们发现理论最大输出记录数= #date * #ISINs * #Exchanges * #FIIs = 2992 * 2955 * 567 * 82268,而实际数字总输出线数仅为5261,942,仅为理论最大值的1.275 * 10 ^( – 8) – 非常稀疏.虽然存在稀疏性,但我们之前做过猜测,但是数组可能非常稀疏 – 这对于内存管理来说非常重要 – 我们无法通知实际数据集.所花费的时间似乎在输入大小中呈指数级增长,但在不会造成实际困难的限度内.谢谢你,艾德. 解决方法
通常,关联数组没有问题.在awk中(除了用于真正2D数组的gawk),具有4个下标的关联数组与具有2个下标的关联数组相同,因为实际上它只有一个下标,其是由SUBSEP分隔的每个伪下标的串联.
鉴于你说我一次无法处理超过6500行.问题远比你编写代码的方式更有可能比任何基本的awk问题更多,所以如果你想要更多帮助,请发布一个带有示例输入和预期输出的小脚本来演示你的问题和尝试的解决方案,看看我们是否有关于改善其内存使用的方法的建议. 鉴于您发布的脚本,我预计问题出在您的END部分中的嵌套循环当您执行以下操作时: for (i=1; i<=maxI; i++) { for (j=1; j<=maxJ; j++) { if ( arr[i,j] != 0 ) { print arr[i,j] } } } 你正在为循环之前不存在的i和j的每个可能组合创建arr [i,j]只是通过测试arr [i,j]!= 0.如果你改为写道: for (i=1; i<=maxI; i++) { for (j=1; j<=maxJ; j++) { if ( (i,j) in arr ) { print arr[i,j] } } } 然后循环本身不会在arr []中创建新条目. 所以改变这个块: if (BNR[date_list[u],fii_list[x]] > 0) { BR = BNR[date_list[u],fii_list[x]] SR = SNR[date_list[u],fii_list[x]] BS = BSH[date_list[u],fii_list[x]] BV = BRV[date_list[u],fii_list[x]] SS = SSH[date_list[u],fii_list[x]] SV = SRV[date_list[u],fii_list[x]] 这可能会不必要地将BNR,SNR,BSH,BRV,SSH和SRV中的每一个变成巨大但高度稀疏的数组,如下所示: idx = date_list[u] SUBSEP isin_list[v] SUBSEP exch_list[w] SUBSEP fii_list[x] BR = (idx in BNR ? BNR[idx] : 0) SR = (idx in SNR ? SNR[idx] : 0) if ( (BR + SR) > 0 ) { BS = (idx in BSH ? BSH[idx] : 0) BV = (idx in BRV ? BRV[idx] : 0) SS = (idx in SSH ? SSH[idx] : 0) SV = (idx in SRV ? SRV[idx] : 0) 如果有帮助,请告诉我们.还要检查代码中您可能正在执行相同操作的其他位置. 当你没有2时,你有4个下标这个问题的原因只是你在循环中有4级嵌套现在创建更大和更稀疏的数组,当你只有2. 最后 – 你的脚本中有一些奇怪的语法,其中一些@MarkSetchell在注释中指出,你的脚本效率不高,因为你没有使用else语句,所以测试多个条件可以可能都是真的并且你正在重复测试相同的条件,并且它不稳定,因为你没有锚定你的RE(例如你测试/ 4 | 1 [13] /而不是/ ^(4 | 1 [13 ])$/ so例如你的4将匹配14或41等而不只是4本身)所以将你的整个脚本改为: $cat tst.awk BEGIN { FS = "," } # For each array subscript variable -- DATE ($10),and number of unique values. $17 ~ /_EQ&;/ { if (!seenDate[$10]++) date_list[++d] = $10 if (!seenIsin[$9]++) isin_list[++i] = $9 if (!seenExch[$12]++) exch_list[++e] = $12 if (!seenFii[$5]++) fii_list[++f] = $5 # For cash-in,RV = rupee-value. idx = $10 SUBSEP $9 SUBSEP $12 SUBSEP $5 if ( $11 ~ /^([12359]|1[24])$/ ) { ++BNR[idx]; BSH[idx] += $15; BRV[idx] += $16 } else if ( $11 ~ /^(4|1[13])$/ ) { ++SNR[idx]; SSH[idx] += $15; SRV[idx] += $16 } } END { print NR,"records processed." print " " printf "%-11st%-13st%-20st%-19st%-7st%-7st%-14st%-14st%-18st%-18sn","DATE","SRV" for (u = 1; u <= d; u++) { for (v = 1; v <= i; v++) { for (w = 1; w <= e; w++) { for (x = 1; x <= f; x++) { #check first below for records with zeroes,don't print them idx = date_list[u] SUBSEP isin_list[v] SUBSEP exch_list[w] SUBSEP fii_list[x] BR = (idx in BNR ? BNR[idx] : 0) SR = (idx in SNR ? SNR[idx] : 0) if ( (BR + SR) > 0 ) { BS = (idx in BSH ? BSH[idx] : 0) BV = (idx in BRV ? BRV[idx] : 0) SS = (idx in SSH ? SSH[idx] : 0) SV = (idx in SRV ? SRV[idx] : 0) printf "%-11st%13st%20st%19st%7dt%7dt%14dt%14dt%18.2ft%18.2fn",date_list[u],SV } } } } } } 我在4个数组名称前添加了一个,因为通过约定数组测试一个值的预先存在通常被命名为see.此外,当填充SNR [] etc数组时,我首先创建了一个idx变量,而不是每次重复使用字段数,以便将来更改它,并且主要是因为字符串连接在awk中相对较慢而且当你使用时会发生什么数组中的多个索引最好只显式地执行字符串连接.并且我将date_list [] etc数组更改为从1开始而不是零,因为所有awk生成的数组,字符串和字段编号从1开始.您可以手动创建一个从0或-357开始的数组或者您想要的任何数字但是如果你总是在1点开始,那么它有一天会让你自己在脚下射击. 我希望通过将嵌套循环限制为可能存在于封闭循环索引组合的值(例如,并非uvw的每个值都可能存在,因此有时候您不应该在x上进行循环),可以提高效率. .例如: $cat tst.awk BEGIN { FS = ",RV = rupee-value. idx = $10 SUBSEP $9 SUBSEP $12 SUBSEP $5 if ( $11 ~ /^([12359]|1[24])$/ ) { seen[$10,$9] seen[$10,$12] ++BNR[idx]; BSH[idx] += $15; BRV[idx] += $16 } else if ( $11 ~ /^(4|1[13])$/ ) { seen[$10,$12] ++SNR[idx]; SSH[idx] += $15; SRV[idx] += $16 } } END { printf "d = %dn",d | "cat>&2" printf "i = %dn",i | "cat>&2" printf "e = %dn",e | "cat>&2" printf "f = %dn",f | "cat>&2" print NR,"SRV" for (u = 1; u <= d; u++) { date = date_list[u] for (v = 1; v <= i; v++) { isin = isin_list[v] if ( (date,isin) in seen ) { for (w = 1; w <= e; w++) { exch = exch_list[w] if ( (date,isin,exch) in seen ) { for (x = 1; x <= f; x++) { fii = fii_list[x] #check first below for records with zeroes,don't print them idx = date SUBSEP isin SUBSEP exch SUBSEP fii if ( (idx in BNR) || (idx in SNR) ) { if (idx in BNR) { bnr = BNR[idx] bsh = BSH[idx] brv = BRV[idx] } else { bnr = bsh = brv = 0 } if (idx in SNR) { snr = SNR[idx] ssh = SSH[idx] srv = SRV[idx] } else { snr = ssh = srv = 0 } printf "%-11st%13st%20st%19st%7dt%7dt%14dt%14dt%18.2ft%18.2fn",date,exch,fii,bnr,snr,bsh,ssh,brv,srv } } } } } } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |