22
$\begingroup$

I have a list of lists of the form:

{{1, 2}, {2, 4}, {2, 8}}

But I want to multiply only the second dimension of that data by a constant. I know I could do this with a loop but that is "dirty". There has to be a better way. For example if I multiply the second dimension by 2 I would get:

{{1, 4}, {2, 8}, {2, 16}}
$\endgroup$
2
  • 1
    $\begingroup$ smartThread[{{1, 2}, {2, 4}, {2, 8}}*{1, 2}] $\endgroup$
    – ssch
    Commented Sep 15, 2013 at 9:59
  • $\begingroup$ Transpose[{1, 2} Transpose[lst]] and ScalingTransform[{1, 2}][lst] are other possibilities. Both from an old MathGroup post by Carl Woll, according to my notes. The first one seems to be very fast. $\endgroup$
    – user1066
    Commented Jan 29, 2018 at 16:18

9 Answers 9

29
$\begingroup$

You can also do it this way:

{{1, 2}, {2, 4}, {2, 8}} /. {x_, y_} -> {x, 2 y}

Which gives:

{{1, 4}, {2, 8}, {2, 16}}

You can change 2 in 2 y to whatever constant you want. This method is flexible because, say you want to multiply the first dimension by a different constant you just put that number in front of x. E.g. Suppose you want to multiply the first dimension by 3 and the second by 2, you simply write:

 {{1, 2}, {2, 4}, {2, 8}} /. {x_, y_} -> {3 x, 2 y}

And this accomplishes the desired task.

EDIT: Using Pure Functions.

{#[[1]], 2 #[[2]]} & /@ {{1, 2}, {2, 4}, {2, 8}}

{First[#], 2 Last[#]} & /@ {{1, 2}, {2, 4}, {2, 8}}
$\endgroup$
18
$\begingroup$
arr = {{1, 2}, {2, 4}, {2, 8}};
arr[[All,2]] *= 2;
$\endgroup$
1
  • $\begingroup$ Based on this principle I favor this answer. However, it should be noted that this modifies arr, so a copy should be modified if the original should be unchanged. $\endgroup$
    – Mr.Wizard
    Commented May 8, 2013 at 2:13
12
$\begingroup$

I like to use Apply for this kind of task.

{#1, 2 #2} & @@@ {{1, 2}, {2, 4}, {2, 8}}
$\endgroup$
12
$\begingroup$

Works nicely:

{{1, 2}, {2, 4}, {2, 8}}.DiagonalMatrix[{1, 2}]

DiagonalMatrix[] is particularly convenient for scaling rows or columns.


From whuber:

Multiplying by a diagonal matrix is fast for up to somewhere between 100 and 1000 columns; beyond that, a solution modeled after Transpose[{1, 2} * Transpose[a]] becomes superior. Even better for such large matrices is a . SparseArray[Band[{1, 1}] -> {1, 2}]; this is superior to both once there are 20 columns or so.

$\endgroup$
2
  • 4
    $\begingroup$ +1. Multiplying by a diagonal matrix is fast for up to somewhere between $100$ and $1000$ columns; beyond that, a solution modeled after Transpose[{1, 2} * Transpose[a]] becomes superior. Even better for such large matrices is a . SparseArray[Band[{1, 1}] -> {1,2}]; this is superior to both once there are $20$ columns or so. $\endgroup$
    – whuber
    Commented May 7, 2013 at 22:06
  • $\begingroup$ @whuber as J. M. made this post wiki please include that comment in the answer. $\endgroup$
    – Mr.Wizard
    Commented May 8, 2013 at 2:14
8
$\begingroup$
Cases[{{1, 2}, {2, 4}, {2, 8}}, {x_, y_} -> {x, 2 y}]

Table[{First@i, Last@i*2}, {i, {{1, 2}, {2, 4}, {2, 8}}}]

{#1, 2 #2} & @@ Transpose@{{1, 2}, {2, 4}, {2, 8}} // Transpose


data = RandomReal[10, {10^6, 2}];
r1 = {#1, 2 #2} & @@ Transpose@data // Transpose; // Timing
r2 = {#1, 2 #2} & @@@ data; // Timing
r1 == r2
(*
{0.109201, Null}
{3.229221, Null}
True
*)
$\endgroup$
4
$\begingroup$

Here's another -general- approach, using MapAt. Maybe not the fastest, but I think it is pretty transparent and configurable:

MapAt[2 # &, list, {1 ;;, 2}]

or (thanks to @RunnyKine)

MapAt[2 # &, list, {All, 2}]

or interestingly, faster:

MapAt[(2 #) &, #, 2] & /@ list

Timings

Using data as in @chyanog's post, I get:

MapAt[2 # &, data, {1 ;;, 2}]; // Timing

{1.078534, Null}

MapAt[(2 #) &, #, 2] & /@ data; // Timing

{0.111480, Null}

(where my machine is about 2x faster than chyanog's, so his solution definitely is faster, by about factor 3) (and it doesn't make sense to compare to all the others, as e.g. DiagonalMatrix is suited for this task very well, but not as configurable)

$\endgroup$
1
  • 1
    $\begingroup$ +1. You can also use {All, 2} in place of {1;;, 2} with MapAt $\endgroup$
    – RunnyKine
    Commented Sep 15, 2013 at 8:37
3
$\begingroup$

As of v13.1, we can also use Threaded:

{{1, 2}, {2, 4}, {2, 8}} Threaded[{1, 2}]
(* {{1, 4}, {2, 8}, {2, 16}} *)
$\endgroup$
3
$\begingroup$

Using ReplaceAt (new in 13.1)

list = {{1, 2}, {2, 4}, {2, 8}};

ReplaceAt[list, x_ :> 2 x, {All, 2}]

{{1, 4}, {2, 8}, {2, 16}}

ReplaceAt has the advantage that we can easily impose additional conditions:

ReplaceAt[list, x_ /; x > 2 :> 2 x, {All, 2}]

{{1, 2}, {2, 8}, {2, 16}}

$\endgroup$
3
$\begingroup$

Using SubsetMap:

list = {{1, 2}, {2, 4}, {2, 8}};
SubsetMap[Times[#, 2] &, list, {All, 2}]

{{1, 4}, {2, 8}, {2, 16}}


To impose more conditions:

SubsetMap[Map[If[# > 2, 2 #, #] &], list, {All, 2}]

{{1, 2}, {2, 8}, {2, 16}}

$\endgroup$

Your Answer

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

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