13
$\begingroup$

I have a list of numerical data, and I want to write a program that will keep the maximum as it is, but reduce the rest by certain factor (such as 10). For example:

input = {2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6}

with this operation yielding

output = {0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06}.

As you can see, I want the program to keep the maximum untouched, which in this case is 9. How can I do it?

$\endgroup$
2
  • $\begingroup$ Hello, welcome to Mathematica.SE! I suggest the following: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2) Read the faq! 3) When you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign! $\endgroup$
    – xzczd
    Dec 23, 2014 at 7:37
  • 3
    $\begingroup$ What if there are multiple elements equal to the maximum? E.g. {5,4,3,4,5} has 5 twice. $\endgroup$
    – Szabolcs
    Dec 23, 2014 at 19:12

11 Answers 11

20
$\begingroup$

We can use Replace to express an almost literal translation of the problem statement:

Replace[input, n : Except[Max[input]] :> n/10., {1}]

(* {0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06} *)

Why Replace instead of /.?

Replace is used instead of ReplaceAll (/.) to ensure that the replacement rule is only applied to the level one list elements ({1}). If we had used the /., then Except[Max[input]] would match the entire list (the level zero expression), which would have ended up dividing the whole list by 10.

Why 10. instead of 10?

Here the approximate divisor 10. is used in order to force all of the result values to be approximate as requested in the question. If the exact integer 10 is used instead, then the results will also be exact numbers (except for the last result, which started from the approximate input value 0.6):

Replace[input, n : Except[Max[input]] :> n/10, {1}]

(* {1/5, 3/10, 1/10, -3/10, -1/2, 9, 1/5, 3/5, -1/10, 0.06} *)
$\endgroup$
13
$\begingroup$

Here is my solution:

input = {2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6};
factor=10.;
pos = First@Ordering[input, -1];
output = ReplacePart[input/factor, pos -> input[[pos]]]

{0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06}

$\endgroup$
1
  • 2
    $\begingroup$ This one is quite fast $\endgroup$ Dec 23, 2014 at 6:23
10
$\begingroup$

At least this is the shortest for so long:

# (1. + 9 UnitStep[# - Max@#])/10 &@input
{0.2, 0.3, 0.1, -0.3, -0.5, 9., 0.2, 0.6, -0.1, 0.06}
$\endgroup$
6
$\begingroup$

Naitree's solution seems like the way to go IMO, but I had fun doing it this way.

Below I temporarily teach Mathematica 9/10 == 9. I didn't profile too much, but this solution seems to be fast.

EmbellishMax = With[{max = Max[#]},
    Internal`InheritedBlock[{Divide},
        Unprotect[Divide];
        Divide[max, 10.] = max;
        Divide[#, 10.]
    ]
]&;

EmbellishMax[{2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6}]
{0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06}
Do[EmbellishMax[{2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6}], {1000}] // AbsoluteTiming
{0.010207, Null}
$\endgroup$
6
$\begingroup$
a = {2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6};

a[[ Ordering[a, -1] ]] *= 10;

a /= 10`
{0.2, 0.3, 0.1, -0.3, -0.5, 9., 0.2, 0.6, -0.1, 0.06}

If either the fact that 9 becomes 9. or in place modification of a is a problem then:

a = {2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6};
b = a;

b /= 10`;
(b[[#]] = a[[#]];) & @ Ordering[a, -1]

b
{0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06}
$\endgroup$
2
  • $\begingroup$ I know this was not mentioned in the original Q, but this does not work for repeated maxima, for example a ={9, 2,9} $\endgroup$
    – Ajasja
    Dec 23, 2014 at 15:06
  • $\begingroup$ Very concise (and fast). +1 :) $\endgroup$
    – Naitree
    Dec 23, 2014 at 17:32
4
$\begingroup$

Not really optimized but it seems to work :

input = {1, 2, 3, 4};
n = Length[input];
pos = Position[input, Max[input]][[1]];
output = Table[input[[i]]/10, {i, 1, n}];
output[[pos]] = Max[input];
output;
$\endgroup$
3
$\begingroup$
f[input_] := input/10. + 9 #/10 KroneckerDelta /@ (input - # ) &@Max@input
f[input]
(*
{0.2, 0.3, 0.1, -0.3, -0.5, 9., 0.2, 0.6, -0.1, 0.06}
*)
$\endgroup$
3
$\begingroup$

ReplaceAll

Shortest:

f1 = .1 (# /. m : Max@# :> 10 m) &;
f1@input
(* {0.2,0.3,0.1,-0.3,-0.5,9.,0.2,0.6,-0.1,0.06} *)

Longer but also faster:

f1b = With[{m = Max@#}, .1 (# /. m -> 10 m)] &; 
f1b@input
(* {0.2,0.3,0.1,-0.3,-0.5,9.,0.2,0.6,-0.1,0.06} *)

MapAt

f2 = With[{m = N@Max@#, p = Position[#, Max@#]}, MapAt[m &, .1 #, p]] &;
f2@input
(* {0.2,0.3,0.1,-0.3,-0.5,9.,0.2,0.6,-0.1,0.06} *)
$\endgroup$
2
$\begingroup$

I used a trick to get it down to one line:

input/10 + 9/10 (input Boole[Thread[input == Max[input]]])

Not sure it'd be very fast on a big list.

Also works:

input*(9/10 (HeavisideTheta@(Boole[Thread[input == Max[input]]] - 1/2)) + 1/10)
$\endgroup$
2
$\begingroup$
lst = {2, 3, 1, -3, -5, 9, 2, 6, -1, 0.6}
f[x_ /; x < Max[lst]] := x/10 // N
f[x_ /; x <= Max[lst]] := x
f[#] & /@ lst

A very crude way of doing things.

$\endgroup$
2
$\begingroup$

Here is another one using MapAt:

MapAt[#/10. &, input, List /@ Ordering[input, Length[input] - 1]]

{0.2, 0.3, 0.1, -0.3, -0.5, 9, 0.2, 0.6, -0.1, 0.06}

$\endgroup$
1
  • $\begingroup$ A minor improvement: MapAt[#/10 &, input, List /@ Most@Ordering[input]] $\endgroup$
    – Syed
    Apr 17 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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