15

Here is my list:

collection = {76.6256, 51.9264, 50.238, 14.4203, 80.9205, 12.2036, 2.39568,
              38.2747, 12.4422, 29.9621}

collection /. {x_ /; x < 50 -> "F"} 

and this gives me

{76.6256, 51.9264, 50.238, "F", 80.9205, "F", "F", "F", "F", "F"}

as expected. But I want to add more rules with ranges like that. I'm not sure how to do this. I've tried:

collection /. {{x_ /; x < 50 -> "F"}, {x_ /; 50 <= x < 56 -> "D"},
               {x_ /; 56 <= x < 71 -> "C"}, {x_ /; 71 <= x < 85 -> "B"},
               {x_ /; 85 <= x <= 100 -> "A"}}

and that yields

{{76.6256, 51.9264, 50.238, "F", 80.9205, "F", "F", "F", "F", "F"},
   {76.6256, "D", "D", 14.4203, 80.9205, 12.2036, 2.39568, 38.2747, 12.4422, 29.9621},
   {76.6256, 51.9264, 50.238, 14.4203, 80.9205, 12.2036, 2.39568, 38.2747,
         12.4422, 29.9621},
   {"B", 51.9264, 50.238, 14.4203, "B", 12.2036, 2.39568, 38.2747,
         12.4422, 29.9621},
   {76.6256, 51.9264, 50.238, 14.4203, 80.9205, 12.2036, 2.39568, 38.2747,
         12.4422, 29.9621}}`

and I've tried a few others which don't even work at all. What is the simplest way of going about this? My real question is, how do I construct a list of rules where I can determine which range (0-50, 50-60, etc.) each number in the list is in?

Sektor
  • 3,320
  • 7
  • 27
  • 36
Sultan of Swing
  • 913
  • 1
  • 6
  • 19
  • Your replacement rules should be a list of rules, not a list of lists. When it's a list mylist of lists, ReplaceAll (/.) returns a list of results, one for each list in mylist. See @belisarius's answer. – Michael E2 Sep 23 '14 at 21:59

7 Answers7

17
Clear[grade]

grade[x_?NumericQ] = Piecewise[{
    {"F", x < 50}, {"D", x < 56},
    {"C", x < 71}, {"B", x < 85}},
   "A"];

collection = {76.6256, 51.9264, 50.238,
   14.4203, 80.9205, 12.2036, 2.39568,
   38.2747, 12.4422, 29.9621};

grade /@ collection

{"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}

Bob Hanlon
  • 157,611
  • 7
  • 77
  • 198
17

Just put all your rules in a list. You don't need the lower bounds. The first matching rule in the list is applied:

s = {x_ /; x < 50 -> "F", x_ /; x < 56 -> "D", x_ /; x < 71 -> "C", 
     x_ /; x < 85 -> "B", x_ /; x <= 100 -> "A"};

collection /. s

(* {"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}*)
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
  • 1
    Crap! I swear I tried this exact same thing and it didn't work for me! But perhaps I forgot a bracket or something. This is definitely the simplest and was exactly what I was looking for. Thank you very much and thanks to everyone who contributed. Your help is really appreciated! – Sultan of Swing Sep 24 '14 at 00:22
10

For a large number of values a better method is to use Interpolation with an order of zero.

First build the InterpolatingFunction:

points = {{50, "F"}, {56, "D"}, {71, "C"}, {85, "B"}, {100, "A"}};

fn = Interpolation[points, InterpolationOrder -> 0];

Then:

fn[collection] // Quiet
{"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}

Performance

I claimed that for "a large number of values" the method above is superior to replacement rules. Here is a BenchmarkPlot to support that assertion:

letters = StringJoin /@ Tuples[CharacterRange["A", "Z"], 3];
numbers = Sort @ RandomSample[Range[100000], 26^3];
points = {numbers, letters}\[Transpose];
rand = RandomReal[100000, 50000];
f1 = ReplaceAll[rand, (x_ /; x < # -> #2) & @@@ #] &;
f2 = Interpolation[#, InterpolationOrder -> 0][rand] &;
Off[InterpolatingFunction::dmval]

Needs["GeneralUtilities`"]

BenchmarkPlot[{f1, f2}, Sort@RandomSample[points, #] &, 2^Range[14], 
 "IncludeFits" -> True, TimeConstraint -> 30]

enter image description here

  • Note the logarithmic scale.

  • The x axis is the number of levels (letters) in the assignment.

  • Time to construct each InterpolatingFunction is included; reuse would be faster.

ReplaceAll is faster with only a few levels, but from eight and up InterpolatingFunction is faster, and with many levels orders of magnitude faster.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
9

More handy in case of more notes (?).

notes = {"F", "D", "C", "B", "A"};
bl = BinLists[collection, {{0, 50, 56, 71, 85, 100}}];

collection /. Flatten@MapThread[Thread[# -> #2] &, {bl, notes}]
{"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}

or

notes = {"F", "D", "C", "B", "A"}
levels = {50, 56, 71, 85, 100}
Block[{x},
 Function[note[x_] := Which[##]] @@ Flatten[MapThread[{x <= #, #2} &, 
                                                      {levels, notes}]]

     ]

note /@ collection
Kuba
  • 136,707
  • 13
  • 279
  • 740
6

You can use Fold to successively apply a list of rules.

Fold[#1 /. #2 &, collection, {{x_ /; x < 50 -> "F"},
    {x_ /; 50 <= x < 56 -> "D"}, {x_ /; 56 <= x < 71 -> "C"},
    {x_ /; 71 <= x < 85 -> "B"}, {x_ /; 85 <= x <= 100 -> "A"}}]
{"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}
paw
  • 5,650
  • 23
  • 31
5
collection /. 
 x_?NumberQ :> Which[x < 50, "F", x < 56, "D", x < 71, "C", x < 85, "B", x <= 100, "A"]

{"B", "D", "D", "F", "B", "F", "F", "F", "F", "F"}

Timing comparison for 1000 random reals repeated 1000 times

enter image description here

eldo
  • 67,911
  • 5
  • 60
  • 168
  • (+1) timings={2.53, 2.71, 3.01, 3.42, 4.68, 5.09, 13.59}; gradesF[{10, Infinity}, Reverse@{"ok", "Ouch"}][timings]:) – kglr Sep 24 '14 at 13:47
  • @kguler your solution has merits (flexibility) which - in many cases - should be more important than seconds :) – eldo Sep 24 '14 at 14:11
  • Please see the timing plot in my answer. It doesn't invalidate your timings but it clarifies and supports what I meant about the performance of Interpolation. – Mr.Wizard Oct 02 '14 at 17:02
4
ClearAll[gradesF];
gradesF[x_: {49, 59, 79, 89, 100}, y_: {"A", "B", "C", "D", "F"}] := 
  Function[{z}, Piecewise[{#, z < #2} & @@@ (Flatten /@ Thread[{Reverse@y, x}])], Listable];

gradesF[][collection]
(* {"C","D","D","F","B","F","F","F","F","F"} *)

gradesF[{50, 56, 71, 85, 100}][collection]
(* {"B","D","D","F","B","F","F","F","F","F"} *)

gradesF[{69, 100}, {"Pass", "Fail"}][collection]
(* {"Pass","Fail","Fail","Fail","Pass","Fail","Fail","Fail","Fail",\
"Fail"} *)
kglr
  • 394,356
  • 18
  • 477
  • 896