22

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}}
Matthew Kemnetz
  • 809
  • 7
  • 13

9 Answers9

29

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}}
RunnyKine
  • 33,088
  • 3
  • 109
  • 176
18
arr = {{1, 2}, {2, 4}, {2, 8}};
arr[[All,2]] *= 2;
Guillochon
  • 6,117
  • 2
  • 31
  • 57
12

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.

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
  • 4
    +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. – whuber May 07 '13 at 22:06
  • @whuber as J. M. made this post wiki please include that comment in the answer. – Mr.Wizard May 08 '13 at 02:14
12

I like to use Apply for this kind of task.

{#1, 2 #2} & @@@ {{1, 2}, {2, 4}, {2, 8}}
einbandi
  • 4,024
  • 1
  • 23
  • 39
8
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
*)
chyanog
  • 15,542
  • 3
  • 40
  • 78
4

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)

Pinguin Dirk
  • 6,519
  • 1
  • 26
  • 36
3

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

{{1, 2}, {2, 4}, {2, 8}} Threaded[{1, 2}]
(* {{1, 4}, {2, 8}, {2, 16}} *)
xzczd
  • 65,995
  • 9
  • 163
  • 468
3

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}}

eldo
  • 67,911
  • 5
  • 60
  • 168
3

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}}

Syed
  • 52,495
  • 4
  • 30
  • 85