12
$\begingroup$

Bug introduced in 7.0 or earlier and persisting through 13.2.0.


For a very simple dataset TableAlignments->Left does not work:

table = {{"chi1", {0.5732772714880409`, 0.37224553853824593`, 
    0.4406080034309774`}}, {"chi2", {0.11746301802400358`, 
    0.2410578341291309`, 
    0.20673964872863448`}}, {"chi3", {Missing["Only 1 points"], 
    Missing["Only 1 points"]}}, {"chi4", {0.05693578150660176`, 
    0.06300145309006996`, 
    0.09833005450944177`}}, {"chi5", {0.08916843025621737`, 
    0.07612462967770783`, 0.05993923221739268`}}}
tableImage = 
 TableForm[table, 
  TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
  TableAlignments -> Left]

Wrong output

TableAlignments->Right seems to work

Correct right-aligned output

Am I missing something simple?

$\endgroup$

5 Answers 5

11
$\begingroup$

The Problem

I believe this is a bug in TableForm. We can see by looking at the Box form of the output that the option ColumnAlignments of the outermost GridBox does not behave as it should.

tab = {{1, {1, 1, 1}}, {2, {2, 2, 2}}, {3, {3, 3, 3}}, {4, {4, 4, 4}}};

getOption = Options[First@ToBoxes@#, ColumnAlignments] &;

tForm =
 TableForm[tab, 
   TableHeadings -> {None, {"Title 1", "Title 2"}}, 
   TableAlignments -> #] &;

Table[
  {ali, getOption @ tForm @ ali},
  {ali, {Left, Center, Right, Top, Bottom}}
] // TableForm

Mathematica graphics

As you can see this option tracks TableAlignments for every value except Left.


A solution

Since this answer is not complete without a work-around:

fix =
  DisplayForm @ Replace[
    ToBoxes @ #,
    (ca : ColumnAlignments -> _) :> (ca -> Left),
    {2}
  ] &;

fix @ tableImage

Mathematica graphics


A fix to load at startup

Here are two options for a fix suitable for inclusion in your init.m file.

Method #1

This is just packaging the simple fix above. It could be problematic because it forces evaluation of TableForm into boxes, when TableForm normally acts as a wrapper. For this reason method #2 should be used if possible.

Unprotect[TableForm];

x : TableForm[__, TableAlignments -> Left | {Left, _}, ___] :=
  Replace[
    MakeBoxes @ x,
    (ca : ColumnAlignments -> _) :> (ca -> Left),
    {2}
  ] // DisplayForm

Protect[TableForm];

Method #2

This is considerably more verbose, but it works at the correct level (MakeBoxes). However, it may be a bit fragile: when a TableForm expression is first displayed (or explicitly sent to MakeBoxes) certain additional definitions are loaded internally. I use such an explicit call, add my definition, and then modify the order of the FormatValues of TableForm. Should there be some other internal initialization that resets the FormatValues or changes their order this fix will be lost. It is important that this code not be evaluated twice in one session or the FormatValues will be out of order and the fix will fail.

Unprotect[TableForm]

TableForm[{}] // ToBoxes; (* pre-load TableForm's FormatValues; do not remove! *)

MakeBoxes[x : TableForm[__, opts : OptionsPattern[]], _] /; ! TrueQ[tfAlignLeftFix] :=
  Block[{tfAlignLeftFix = True},
    Replace[MakeBoxes@x, (ca : ColumnAlignments -> _) :> (ca -> Left), {2}]
  ] /; MatchQ[OptionValue[TableForm, {opts}, TableAlignments], Left | {Left, _}]

FormatValues@TableForm = RotateRight@FormatValues@TableForm;

Protect[TableForm]

This method also adds robust detection of the alignment option, e.g. including those set with Options[TableForm] = . . . and of the form TableForm[_, {options}].

$\endgroup$
7
  • $\begingroup$ Could you comment on how the last fix works? Here is what I understand: Any time it sees a TableForm with TableAlignments->Left it converts the expression to boxes and replaces the ColumnAlignmets, then it converts this back to DisplayForm. What is the purpose of x: and /;? x is probably the whole matched expression and the conditional assignment is there to prevent recursion? $\endgroup$
    – Ajasja
    Jul 4, 2012 at 9:15
  • $\begingroup$ @Ajasja You got it right. All of it. Well done. :-) (ps. sorry for the ironic out-of-alignment code; that's a bug with $ that I have reported on Meta.) $\endgroup$
    – Mr.Wizard
    Jul 4, 2012 at 9:16
  • $\begingroup$ Thanks. But what could cause recursion? Nested TableForms? $\endgroup$
    – Ajasja
    Jul 4, 2012 at 9:25
  • 1
    $\begingroup$ @Ajasja Yes. Since x is the original function call to TableForm unchanged and it is given to ToBoxes within the fix, without the Block this would again match the pattern given and start an infinite recursion. There are other ways around this problem but this one is nice and clean. I learned it here. $\endgroup$
    – Mr.Wizard
    Jul 4, 2012 at 9:30
  • $\begingroup$ @Ajasja your recent reference (thanks) make me look at this answer again, and I now realize that Gayley-Villegas recursion protection is overkill, as a simple Unevaluated@x works also. Further, I believe we can simple use MakeBoxes as this already holds its arguments. I shall update my answer accordingly. $\endgroup$
    – Mr.Wizard
    Aug 24, 2012 at 8:25
8
$\begingroup$

For some reason, your nested data rows throw off the alignment. If you Column your second entries, you get this:

table[[All, 2]] = Column /@ table[[All, 2]];
TableForm[table, 
 TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
 TableAlignments -> {Left}]

Correct output

Personally, I mostly switched over to Grid because it is more versatile, but of course TableForm is still supported and useful...

$\endgroup$
3
  • $\begingroup$ @Ajasja Thanks for the prettifying edit. I prefer to include small amounts of raw data in the code snippet for ease of use. All the other posters seem to go along with table directly, so I am the odd one out there. $\endgroup$
    – Yves Klett
    Jul 3, 2012 at 9:48
  • $\begingroup$ You're welcome. Also I agree with the last sentence. I think I'll have to switch over to Grid as well. $\endgroup$
    – Ajasja
    Jul 3, 2012 at 12:06
  • $\begingroup$ @Ajasja Nesting Grids works really quite nicely in a lot of cases... $\endgroup$
    – Yves Klett
    Jul 3, 2012 at 12:17
7
$\begingroup$

The following modification of your code gives what you expect:

TableForm[Map[TableForm[#, TableAlignments -> Left] &, table, {2}], 
TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
TableAlignments -> Left]

enter image description here

EDIT: A variation applying Column to the second elements of the lists (as in @Yves's answer):

TableForm[Map[{First@#,Column @@ Rest[#]}&, table], 
TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
TableAlignments -> Left]
$\endgroup$
0
5
$\begingroup$

The trouble seems to be with the headings specification:

TableForm[table, TableAlignments -> Left]

seems to work fine.

Adding the headings as an element to the table will work:

Prepend[table, {"Dihedral", "\[Tau] per state [ns]"}] // TableForm

or

 TableForm[Prepend[table, {"Dihedral", "\[Tau] per state [ns]"}],TableAlignments -> Left]

Produce,

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ As soon as you add the column headings it stops working as expected. For example: TableForm[List /@ table[[All, 2]], TableHeadings -> {table[[All, 1]], {"\[Tau] per state [ns]"}}] $\endgroup$
    – Ajasja
    Jul 3, 2012 at 9:23
  • $\begingroup$ And it only breaks for the default option of Left alignment and works for Right and Center. $\endgroup$ Jul 3, 2012 at 9:31
2
$\begingroup$

I think it is aligning correctly, i.e. each block corresponding to your chis. For instance, with TableAlignments -> Right the last entry corresponding to chi2 is not aligned. If you flatten them you get what you'd expect :

mydata = Flatten[table[[All, 2]]];
TableForm[Transpose[{Range[Length[mydata]], mydata}], 
  TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
  TableAlignments -> {Left}]
$\endgroup$
1
  • $\begingroup$ Thanks, but I have to keep the values grouped together. $\endgroup$
    – Ajasja
    Jul 3, 2012 at 9:15

Your Answer

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

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