Skip to main content

Displaying index as subscript on output: e.g. C[i] -> C_i with Notation[...] or Interpretation[..]?


You have all convinced me not to type in formulas with subscripts. However, in order to be able to match the subscripts in my whiteboard math, I figure a 2nd best solution is to want to display instances of C[i] with $C_i$ , only in the output, for a chosen set of $C$ and for any $i$


DisplaySubscripted[val_] := 
Format[val[args__]] := Interpretation[Subscript[val, args], val[args]]
SubscriptVariables[valueList_] := Scan[DisplaySubscripted, valueList];

(* Usage*)
SubscriptVariables[{a, b}]; (* Label the variables I want to display subscripted*)

Solve[a[1] + a[2] - 2 b[2] == c[1], b[2]]
(* This subscripts the a[1], a[2], b[2] and not the c[1], as I hoped.
It also doesn't seem to mess up the symbolics*)

Am I missing something, or is this a side-effect free approach to this problem? (it also works well with Subsuperscript and a[1][2] displaying as $a_1^2$ and it doesn't seem to mess up the superscript with powers)


ALTERNATIVELY: I also thought that Notation may be a way to do this. Easy enough to do this for an arbitrary variable. e.g.


Needs["Notation`"];
DisplaySubscripted[var_] :=
Notation[ParsedBoxWrapper[
SubscriptBox[var, "1"]]\[DoubleLongLeftArrow]ParsedBoxWrapper[

RowBox[{var, "[", "1", "]"}]]];
DisplaySubscripted["f"];

f[1] (* Displays in output properly as f_1*)

However, I can't figure out how to match this for arbitrary subscript patterns. The obvious one does not work: Notation[ParsedBoxWrapper[ SubscriptBox[var, "_"]]\[DoubleLongLeftArrow]ParsedBoxWrapper[ RowBox[{var, "[", "_", "]"}]]]


I am also not entirely sure that passing var_ as strings is the best way to do it, but if I use DisplaySubscripted[f] instead, it displays the namespace (i.e. Global'f_1). If the Format approach I gave first works, it seems much simpler.



Answer



I think your approach is fine; in fact I just recommended something similar in another answer.


However, for the best handing of using your formatted output as input may want to use MakeBoxes for the reason described by Michael Pilat.



You also might consider putting a list your subscripted symbols in a global variable for easy changes, or Blocking.


Something like this:


MakeBoxes[a : h_[args__], fmt_] /;
MemberQ[$subs, Unevaluated @ h] :=
ToBoxes[Interpretation[Subscript[h, args], a], fmt]

$subs = Hold[a, b];

expr = Solve[a[1] + a[2] - 2 b[2] == c[1], b[2]]


Block[{$subs = {c}}, ToString[expr, StandardForm]]

enter image description here


Notes:




  • I used ToString to cause MakeBoxes to trigger inside the Block; this is similar to what I did for Returning an unevaluated expression with values substituted in.




  • I put the Symbols inside Hold so that global definitions would not conflict; the MakeBoxes definition is written to handle this. A list can also be used as shown in the Block example.







Extension


Matching symbols by pattern was requested. I believe it is best to use string patterns, and require all symbols (in the list) to be in String form:


MakeBoxes[a : h_[args__], fmt_] :=
ToBoxes[Interpretation[Subscript[h, args], a], fmt] /;
MemberQ[Names @ $subs, ToString @ Unevaluated @ h]

$subs = {"a", "b*"};


expr = Solve[a[1] + a[2] - 2 b[2] + bx[1]^2 == c[1], b[2]]

The patterns in $subs can be anything that is accepted by Names.


If you want to handle both literal Symbols and string pattern you will need to convert the Symbols to strings. The easiest way would be Names[ToString /@ $subs] but this would preclude using StringExpression patterns and would fail to hold Symbols unevaluated; I recommend just using strings as above.


In version 7 if I Copy As > LaTeX the expression above I get LaTeX with subscripts:


$b_2\to \frac{1}{2} \left(a_1+a_2+\left(\text{bx}_1\right){}^2-c[1]\right)$


In version 9 this does not work right; you could leave out Interpretion to fix the problem at the expense of being able to copy&paste the expression within Mathematica:


MakeBoxes[a : h_[args__], fmt_] := 
ToBoxes[Subscript[h, args], fmt] /;

MemberQ[Names@$subs, ToString@Unevaluated@h]



Refactored full definitions


Here is refactored code for the generation of your full set of definitions.




  • The code is far more concise and easily modified; one can make a change in one place rather than a dozen.





  • I noted that repeated calls to Names was the cause of a considerable slow-down so I changed that to StringMatchQ.




  • I Protected the symbols {bar, hat, tilde, vec, underbar, plus, minus, star} because I don't think you'll want those accidentally evaluating to something else.




  • I rewrote definitions that call Subsuperscript to use argsub_ rather than argsub__ as the latter could result in invalid input to Subsuperscript.




  • If you use the following code outside of package, without Begin and End, you should add SetAttributes[makeDef, HoldAll] to prevent things like h from possibly evaluating incorrectly.





The Symbols used before Begin are created in the present (typically Global`) context; the usage messages are therefore not superfluous, but may be replaced with merely $scriptedconstants; $scriptedfunctions; if desired.


With[{symbols := Sequence[bar, hat, tilde, vec, underbar, plus, minus, star]},
Unprotect @ symbols; ClearAll @ symbols; Protect @ symbols;
]

$scriptedconstants::usage = "a list of string patterns for symbols: constants";
$scriptedfunctions::usage = "a list of string patterns for symbols: functions";


Begin["formattingRules`"];

makeDef[pat_, body_] := (
MakeBoxes[a : pat, fmt_] := ToBoxes[Interpretation[body, a], fmt] /;
StringMatchQ[ToString @ Unevaluated @ h, $scriptedconstants];
MakeBoxes[a : pat[sub_], fmt_] := ToBoxes[Interpretation[body[sub], a], fmt] /;
StringMatchQ[ToString @ Unevaluated @ h, $scriptedfunctions]
)

set1 = {

bar -> OverBar,
hat -> OverHat,
tilde -> OverTilde,
vec -> OverVector,
underbar -> UnderBar,
plus -> SuperPlus,
minus -> SuperMinus,
star -> SuperStar
};


set2 = {
plus -> "+",
minus -> "-",
star -> "*"
};

makeDef[h_[#], #2[h]] & @@@ set1;
makeDef[h_[argssub__][#], Subscript[#2[h], argssub]] & @@@ Take[set1, 5];
makeDef[h_[argsub_][#], Subsuperscript[h, argsub, #2]] & @@@ set2;
makeDef[h_[argssub__], Subscript[h, argssub]];


End[];

(You may notice a funny offset in lines containing $ above; this is a known bug in the SE rendering.)


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....