Making number of elements in a list smaller, but keeping the same sum and "ratios" between elements

Hello everyone!

I’m trying to write a function that scales the size of a list (specifically in my case containing amplitudes, but I’m trying to write a general-purpose function), while keeping the ratios between the elements in the list “true” to the original list.

For example if I want list_a containing 10 values to become list_b containing 8 values, I know that the first element in the new list has to be list_a[0] + list_a[1]*0.25. 0.25 because it needs 25% of the element at index1 to keep the “ratios” from the first list.
I tried writing a code that simulates this logic of getting percentages from the indexes through the first list, but it’s giving me a real headache, and there has to be a simpler way to do this?
My code looks like this:

(
var list = [1,2,3,1,2,3,1,2,4,5];
var div = 8;
var incr = list.size/div;
var out = Array(div);
var current = 0;
div.do{
	var from = current.value;
	var to = current.value + incr;
	var intDist = ~distance.value(from.asInt, to.asInt);
	var realDist = ~distance.value(from, to);
	var modulo1 = 1-from%1;
	var modulo2 = to%1;
	var temp_val = 0;

	realDist.postln;
	if(intDist == 0, {temp_val = temp_val + (list[from.asInt]*realDist)}, {
		if(intDist == 1, {
			if(modulo1 == 0.0, {
				temp_val = temp_val + list[from.asInt];
				temp_val = temp_val + ((modulo2)*list[to.asInt]);
			}, {
				temp_val = temp_val + ((modulo1)*list[from.asInt]);
				temp_val = temp_val + ((modulo2)*list[to.asInt]);
			});
			if(modulo2 == 0.0, {
				temp_val = temp_val + list[to.asInt];
				temp_val = temp_val + ((modulo1)*list[from.asInt]);
			});
		});
		if(intDist > 1, {
			if(modulo1 == 0.0, {
				temp_val = temp_val + list[from.asInt];
				temp_val = temp_val + (list[from.asInt+1..to.asInt-1].sum);
				temp_val = temp_val + ((modulo2)*list[to.asInt]);
			}, {
				temp_val = temp_val + ((modulo1)*list[from.asInt]);
				temp_val = temp_val + (list[from.asInt+1..to.asInt-1].sum);
				temp_val = temp_val + ((modulo2)*list[to.asInt]);
			});
			if(modulo2 == 0.0, {
				temp_val = temp_val + ((modulo1)*list[from.asInt]);
				temp_val = temp_val + (list[from.asInt+1..to.asInt-1].sum);
				temp_val = temp_val + list[to.asInt];
			});
		});
	});
	out.add(temp_val);
	current = current + incr;
	temp_val;
};
out;
)

I know my code is a total mess, but maybe someone understands what I’m trying to do and knows a simpler way of doing this?

Martin

how bout this?

{
	| array newSize |
	array.stutter(newSize).clump(array.size).collect(_.sum)/newSize
}
1 Like

It sounds like you’re describing resampling with linear interpolation, which is already implemented by resamp1.

Downsampling will inevitably lose information though:

x = [1,2,3,1,2,3,1,2,4,5];

x.blendAt(1.25);  // your 25%
-> 2.25

x.blendAt(2.5);  // next step, halfway between
-> 2

x.blendAt(3.75);
-> 1.75

… and your upward sawtooth pattern has become a lower-amplitude downward sawtooth.

In any case, resamp1 should do it all for you, and if not, blendAt will do the linear interpolation part.

hjh

1 Like

Exactely! I’m very impressed you understood what I meant

I tried resamp1, but it didn’t give me exactely what I was after. Maybe I have to use it in combination with blendAt?

I think I misunderstood what you meant by ratios being preserved. Perhaps resamp1 isn’t the right answer… never mind :man_shrugging:

hjh