10
$\begingroup$

I want to subtract a constant value from a list and if the result is less than 0 then the corresponding list entry should be 0.

Here is my code:

list = {2, 3, 5, 4, 6, 7, 3, 7, 5, 4};

value = 5;

n = Length[list];

Do[
 If[list[[i]] - value < 0, list[[i]] = 0];
 , {i, 1, n}
 ]

list

{0, 0, 5, 0, 6, 7, 0, 7, 5, 0}

How would you solve this, without a loop?

$\endgroup$
17
$\begingroup$

I would use Clip:

Clip[{2,3,5,4,6,7,3,7,5,4}, {5, Infinity}, {0, 0}]

{0, 0, 5, 0, 6, 7, 0, 7, 5, 0}

$\endgroup$
15
$\begingroup$

You could also use Threshold

list = {2, 3, 5, 4, 6, 7, 3, 7, 5, 4};

value = 5;

For the case of integers

Threshold[list, value - 1]

(* {0, 0, 5, 0, 6, 7, 0, 7, 5, 0} *)

EDIT: Comparative timings

kumar[list_, value_] := UnitStep[list - value] list

hanlon[list_, value_] := Threshold[list, value - 1]

alan[list_, value_] := If[# < value, 0, #] & /@ list

woll[list_, value_] := Clip[list, {value, ∞}, {0, 0}]

nasser[list_, value_] := 
 ReplacePart[list, Position[list, x_ /; x - value < 0] -> 0]

{#, RepeatedTiming[Do[temp = #[list, value], {10000}]; temp]} & /@ {hanlon, 
   woll, alan, kumar, nasser} // Grid

enter image description here

$\endgroup$
  • $\begingroup$ Thanks a lot... also for the speed comparison. $\endgroup$ – lio Oct 18 '17 at 4:53
14
$\begingroup$

Using UnitStep

UnitStep[list - value] list

{0, 0, 5, 0, 6, 7, 0, 7, 5, 0}

$\endgroup$
4
$\begingroup$

I like Anjan solution best. But here is another way

list={2,3,5,4,6,7,3,7,5,4};
value=5;
ReplacePart[list, Position[list,x_/;x-value<0]->0]

Mathematica graphics

$\endgroup$
4
$\begingroup$

If you really want to replace the list items, then I would do

list[[Catenate@Position[list, x_ /; x < 5]]] = 0

However, if a new list is acceptable (and this is usually a better practice), then I would view this as a simple map:

If[# < 5, 0, #] & /@ list
$\endgroup$
3
$\begingroup$

This may work for you too:

 list = {2, 3, 5, 4, 6, 7, 3, 7, 5, 4};
 value = 5;
 list /. x_ /; x < value :> 0

{0, 0, 5, 0, 6, 7, 0, 7, 5, 0}

$\endgroup$
2
$\begingroup$

I tried to define a function an then map over lists

aaa = {2, 7, 6, 0, 11}

f[c_] := If[# - c > 0, # - c, 0] &

f[4] /@ aaa
{0, 3, 2, 0, 7}
$\endgroup$
2
$\begingroup$

Here's yet another one, a simple alternative to Threshold. It's slower than some of the other ones for integer input, but it's actually the fastest if your values are Reals:

f[list_, value_] := Chop[list, value]

Just to be clear, for integer input, you'd need use N@list and then convert the result back to an integer via some form of rounding, which slows down the function.

Running Bob's speedtest:

kumar[list_, value_] := UnitStep[list - value] list
hanlon[list_, value_] := Threshold[list, value - 1]
alan[list_, value_] := If[# < value, 0, #] & /@ list
woll[list_, value_] := Clip[list, {value, \[Infinity]}, {0, 0}]
nasser[list_, value_] := ReplacePart[list, Position[list, x_ /; x - value < 0] -> 0]
ender[list_, value_] := Chop[list, value]

list = N@{2, 3, 5, 4, 6, 7, 3, 7, 5, 4};
value = 5;

{#, RepeatedTiming[Do[temp = #[list, value], {10000}]; 
     temp]} & /@ {hanlon, woll, alan, kumar, nasser, ender} // Grid

enter image description here

The integer variant clocks in at about half the speed of Bob's Threshold solution.

It's worth noting that this assumes that the values are all positive (because it would also keep values less than -value).

$\endgroup$
1
$\begingroup$

Late to the party but mapping a function along your list is a robust strategy for replacing loops ie,

ls={2, 3, 5, 4, 6, 7, 3, 7, 5, 4};    
f[n_]:=If[n-5<0,0,n];
f/@ls

It's not as terse as the pure functions but has some clarity for beginners, especially if they don't know a built-in function exists.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.