Re: Efficient function to accumulate a list of
- To: mathgroup at smc.vnet.net
- Subject: [mg132514] Re: Efficient function to accumulate a list of
- From: Bob Hanlon <hanlonr357 at gmail.com>
- Date: Fri, 4 Apr 2014 03:57:58 -0400 (EDT)
- Delivered-to: l-mathgroup@mail-archive0.wolfram.com
- Delivered-to: l-mathgroup@wolfram.com
- Delivered-to: mathgroup-outx@smc.vnet.net
- Delivered-to: mathgroup-newsendx@smc.vnet.net
- References: <20140403061721.737146A1A@smc.vnet.net>
Your function does not work as described. For comparison with other approaches, I have simplified it and modified it to do what you described. Clear[particleToDensity, particleToDensity2, particleToDensity3] Use Module to localize the transient variable names, delete the unnecessary Round and If, and use lower case letter to start a user-defined function (avoid potential future name conflict with built-in functions): particleToDensity[particles_, arrayDimensions_: {144, 300}] := Module[{arr, l, tp}, arr = ConstantArray[0, arrayDimensions]; For[l = 1, l <= Length[particles], l++, tp = particles[[l]]; arr[[tp[[2]], tp[[3]]]] += tp[[1]]]; arr] Use Map rather than For loop: particleToDensity2[particles_, arrayDimensions_: {144, 300}] := Module[{arr}, arr = ConstantArray[0, arrayDimensions]; (arr[[#[[2]], #[[3]]]] += #[[1]]) & /@ particles; arr] Use SparseArray particleToDensity3[particles_, arrayDimensions_: {144, 300}] := SparseArray[{#[[1, 2]], #[[1, 3]]} -> Total[#[[All, 1]]] & /@ GatherBy[particles, #[[{2, 3}]] &], arrayDimensions] data = {{3.6, 7, 4}, {3.4, 8, 6}, {2.1, 7, 4}}; t1 = Timing[arr1 = particleToDensity[data];]; t2 = Timing[arr2 = particleToDensity2[data];]; t3 = Timing[arr3 = particleToDensity3[data];]; Verifying that the arrays are equivalent arr1 == arr2 == arr3 True Map is marginally quicker and the sparse array approach is much quicker {t1[[1]]/t2[[1]], t1[[1]]/t3[[1]]} {1.03, 2.7} However, if the array is relatively dense data = Table[{ RandomReal[{0, 5}], RandomInteger[{1, 144}], RandomInteger[{1, 300}]}, {10000}]; t1 = Timing[arr1 = particleToDensity[data];]; t2 = Timing[arr2 = particleToDensity2[data];]; t3 = Timing[arr3 = particleToDensity3[data];]; Verifying that the arrays are equivalent arr1 == arr2 == arr3 True Map is now noticeably quicker but the SparseArray became noticeably slower than your approach. {t1[[1]]/t2[[1]], t1[[1]]/t3[[1]]} {1.3822, 0.7310} Bob Hanlon On Thu, Apr 3, 2014 at 2:17 AM, <julian.w.francis at gmail.com> wrote: > Dear all, > > I am struggling with how to convert a list of (value, coords) tuples into > an array such that the an element in the array should represent the sum of > all value elements in that list with matching coords, and zero if no > matches, e.g. > > { {3.6, 7,4}, {3,4, 8,6}, {2.1, 7,4} } > > if it were a 10X10 element array would become all zero's, except element > (7,4)->(3.6+2.1) and (8,6)->3.4 > > I've got some code which does what I want: > > ParticleToDensity[particles_] := ( > arr = ConstantArray[0, {144, 300}]; > For[l = 1, l < NoParticles, l++, > (tp = particles[[l]]; > If[tp[[2]] != 0, > arr[[Round[tp[[3]]], Round[tp[[2]]]]] += tp[[1]]] > )]; > arr) > > So, the argument to the function is a list of "particles", and the first > element of this particle is the value in the array I'd like to accumulate, > and the next two elements in that particle say where in the array they > should be accumulated. > > It seems to work, but it is slow. I can see that this isn't really the > Mathematica way of doing things, but I'm struggling to think of a better > way. > > For reference, NoParticles is typically set to something like 10,000, and > it takes about 0.1 secs to process on my machine. However this needs to be > done frequently, hence I am hoping for a better way. > > > > Any help greatly appreciated. > > Thanks, > Julian. > >
- References:
- Efficient function to accumulate a list of {value,coord} into array
- From: julian.w.francis@gmail.com
- Efficient function to accumulate a list of {value,coord} into array