As far as I understand, the Spline object is the result of some approximation or interpolation, it is not for directly defining functions.

That's a pity, as that is exactly what I'd need to analyze some tax schemes. Maybe because I could not define non-overlapping intervals (all could be open on, say, the right end), my version below produces a function (?) that more-or-less restates the cases in the definition but does not evaluate for a simple real scalar argument.

What is a neater and foolproof way to accomplish this, then?

A trivial example works:

Tax [z_]:= Piecewise[{{0,z < 11265},{1,z >=11265 }}]

This does not:

Tax [z_]:= Piecewise[{
{0,z \[Element] Interval[{0 , 11265}]},
{0+ 0.0856(z-11265),z  \[Element]Interval[ {11265,13173}]   },
{1127.6088+0.0963(z-13173),z \[Element] Interval[ {13173,15009}]},
{1445.3667+0.107(z-15009),z \[Element] Interval[ {15009,16881}]},
{1806.267+0.1177(z-16881),z \[Element] Interval[ {16881,18753}]},
{   2207.2281+0.1284(z-18753),z \[Element] Interval[ {18753,20625}]},       
{2648.25+0.1498(z-20625),z\[Element] Interval[{ 20625,22569}]},
{3380.8362+0.1712(z-22569),z \[Element] Interval[ {22569,24513}]},  
{4196.6256+0.1926(z-24513),z \[Element] Interval[ {24513,26457}]},
{5095.61823+0.214(z-26457),z \[Element] Interval[{ 26457,28401}]},
{6077.814+0.2354(z-28401),z\[Element] Interval[ {28401,30345}]},
{   7143.213+0.2568(z-30345),z \[Element] Interval[ {30345,32289}]},
{8291.8152+0.2782(z-32289),z \[Element] Interval[ {32289,34233}]}   ,
{   9523.6206+0.2996(z-34233),z \[Element] Interval[ {34233,36177}]},
{10838.6292+0.321(z-36177),z \[Element] Interval[{ 36177,38121}]},  
{12236.841+0.3424(z-38121),z\[Element] Interval[ {38121,40065}]},
{13718.256+0.3638(z-40065),z \[Element] Interval[ {40065,42009}]},
{15282.8742+0.3852(z-42009),z \[Element] Interval[{ 42009,43953}]},
{16930.6956+    0.4066(z-43953),z\[Element] Interval[ {43953,45897}]},
{18661.7202+0.4173(z-45897),z \[Element] Interval[ {45897,100002}]},
{   41730.8346+0.428(z-100002),z \[Element] Interval[ {100002,150000}]},
{64200+0.4469(z-150000),z \[Element] Interval[ {150000,200004}]},
{89381.7876+0.4578(z-200004),z > 200004 }}]

1 Answer 1


Use IntervalMemberQ[Interval[{a,b}], z] or a<=z<=b instead of Element[z,Interval[{a, b}]]:

Tax2[z_] := Tax[z] /. Element[a_, b_Interval] :> IntervalMemberQ[b, a]


Tax3[z_] := Tax[z] /. Element[z, Interval[{a_, b_}]] :> a <= z <= b


Update: Working with 3 lists as input (1) income thresholds (2) marginal tax rate in each bracket, (3) and lump-sum minimum tax in each bracket, and constructing a tax function using a combination of UnitStep and Dot:

thresholds = Append[Cases[Tax[z], Interval[{a_, b_}] :> a, All], 200004];
marginalrates = Prepend[Cases[Tax[z], a_ Plus[_, z] :> a, All], 0];
lumpsums = Join[{0, 0}, Cases[Tax[z], Plus[a_, Times[_, Plus[_, z]]] :> a, All]];

brackets = Partition[Append[thresholds, Infinity], 2, 1];
ClearAll[taX, boole]
boole[z_] := (1 - UnitStep[-#[[2]] + z]) UnitStep[-#[[1]] + z] & /@ brackets;
taX[z_] := (lumpsums + marginalrates (z - thresholds)).boole[z]

And @@ (taX[#] == Tax2[#] & /@ RandomInteger[{0, 250000}, 1000])


Note: The tax function in OP is not continuous; there are upward jumps at threshold points. Since the three input lists are extracted from OP's Tax function, taX also exhibits the same jumps.

Update 2: To get a continuous tax function we can work with the lists thresholds and marginalrates as follows:

cTax[z_?NumericQ] := NIntegrate[marginalrates.boole[t], {t, 0, z}]
Plot[{taX[z], cTax[z]}, {z, 0, 70000}, PlotLegends -> "Expressions"]

enter image description here

  • $\begingroup$ Nice. By the way, it might be even better to do away with the repetition of the knots and the constant terms that are supposed to ensure continuity at those kinks. If I (or a tax accountant) made any typo in the manual calculations, we might not even end up with a continuous piecewise linear function (spline). $\endgroup$
    – László
    Commented Jul 1, 2019 at 15:26
  • 1
    $\begingroup$ @László To wit, shouldn't the 1127.6088 in the third formula be 163.325? $\endgroup$
    – Michael E2
    Commented Jul 1, 2019 at 18:16
  • 1
    $\begingroup$ @László, please see the update. $\endgroup$
    – kglr
    Commented Jul 2, 2019 at 22:27
  • $\begingroup$ This is very nice, @kglr, if only I could upvote twice. Sorry about the dicontinuous jumps, though, the lump-sum minimums are not as quirky as the look in OP, which must be buggy. Think of the thresholds/knots where higher MTRs should kick in, otherwise the schedule is supposed to be continuous. Does that allow for a simpler construct? $\endgroup$
    – László
    Commented Jul 3, 2019 at 9:30

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.