Repository: pandas-dev/pandas

Issue:
hierarchical index + frame_table + data_columns=True -> TypeError
```
import pandas as pd, numpy

r = numpy.empty((3,4))
index = pd.MultiIndex.from_tuples([('A','a'), ('A','b'), ('B','a'), ('B','b')])
df = pd.DataFrame(r, columns=index)

store = pd.HDFStore('df.h5')
store.put('data',df) #->OK
store.put('data1',df,table=True) #-> Ok
store.put('data2',df,table=True,data_columns=['A']) #-> Ok
store.put('data3',df,table=True,data_columns=True) #-> raises hell 
#TypeError: not all arguments converted during string formatting

store['data']['A'] #->OK
store['data1']['A'] #-> KeyError KeyError: u'no item named A'

In [43]: df = store['data']

In [44]: df
Out[44]: 
   A                            B               
   a             b              a              b
0  2 -1.727234e-77  2.964394e-323   0.000000e+00
1  0  0.000000e+00   0.000000e+00   0.000000e+00
2  0  0.000000e+00   0.000000e+00  8.344027e-309

In [45]: df = store['data1']

In [46]: df
Out[46]: 
   (A, a)        (A, b)         (B, a)         (B, b)
0       2 -1.727234e-77  2.964394e-323   0.000000e+00
1       0  0.000000e+00   0.000000e+00   0.000000e+00
2       0  0.000000e+00   0.000000e+00  8.344027e-309


pd.__version__ -> '0.12.0'
```



Discussion:
hmm might be a bug; just specify the data columns that you need in any event

http://pandas.pydata.org/pandas-docs/dev/io.html#storing-multi-index-dataframes

Once again, man: an instant reply. Thanks so much! Sorry I had to edit the code several times. I was copying and pasting from by REPL and my Editor and made a hash of it. I verified that the code as it stands now replicates the issue. 

~~Also, exploiting you kindness, could you send me a link to how I can use MultiIndex for the `select` and `select_as_multiple usages`?~~

I see your link. Thanks!

Thanks again!!

yep...this a bug....actually wasn't really tested with a column mi


Resolving patch:
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -15,6 +15,7 @@
 import warnings
 
 import numpy as np
+import pandas
 from pandas import (Series, TimeSeries, DataFrame, Panel, Panel4D, Index,
                     MultiIndex, Int64Index, Timestamp)
 from pandas.sparse.api import SparseSeries, SparseDataFrame, SparsePanel
@@ -1379,11 +1380,7 @@ def update_info(self, info):
         for key in self._info_fields:
 
             value = getattr(self, key, None)
-
-            try:
-                idx = info[self.name]
-            except:
-                idx = info[self.name] = dict()
+            idx = _get_info(info, self.name)
 
             existing_value = idx.get(key)
             if key in idx and value is not None and existing_value != value:
@@ -2783,7 +2780,10 @@ def validate_data_columns(self, data_columns, min_itemsize):
         if not len(self.non_index_axes):
             return []
 
-        axis_labels = self.non_index_axes[0][1]
+        axis, axis_labels = self.non_index_axes[0]
+        info = self.info.get(axis,dict())
+        if info.get('type') == 'MultiIndex' and data_columns is not None:
+            raise ValueError("cannot use a multi-index on axis [{0}] with data_columns".format(axis))
 
         # evaluate the passed data_columns, True == use all columns
         # take only valide axis labels
@@ -2879,6 +2879,11 @@ def create_axes(self, axes, obj, validate=True, nan_rep=None, data_columns=None,
                         if sorted(append_axis) == sorted(exist_axis):
                             append_axis = exist_axis
 
+                # the non_index_axes info
+                info = _get_info(self.info,i)
+                info['names'] = list(a.names)
+                info['type'] = a.__class__.__name__
+
                 self.non_index_axes.append((i, append_axis))
 
         # set axis positions (based on the axes)
@@ -3459,10 +3464,20 @@ def read(self, where=None, columns=None, **kwargs):
         if not self.read_axes(where=where, **kwargs):
             return None
 
+        info = self.info.get(self.non_index_axes[0][0],dict()) if len(self.non_index_axes) else dict()
         index = self.index_axes[0].values
         frames = []
         for a in self.values_axes:
-            cols = Index(a.values)
+
+            # we could have a multi-index constructor here
+            # _ensure_index doesn't recognized our list-of-tuples here
+            if info.get('type') == 'MultiIndex':
+                cols = MultiIndex.from_tuples(a.values)
+            else:
+                cols = Index(a.values)
+            names = info.get('names')
+            if names is not None:
+                cols.set_names(names,inplace=True)
 
             if self.is_transposed:
                 values = a.cvalues
@@ -3657,6 +3672,14 @@ class AppendableNDimTable(AppendablePanelTable):
     obj_type = Panel4D
 
 
+def _get_info(info, name):
+    """ get/create the info for this name """
+    try:
+        idx = info[name]
+    except:
+        idx = info[name] = dict()
+    return idx
+
 def _convert_index(index, encoding=None):
     index_name = getattr(index, 'name', None)
 


Test patch:
diff --git a/pandas/io/tests/test_pytables.py b/pandas/io/tests/test_pytables.py
--- a/pandas/io/tests/test_pytables.py
+++ b/pandas/io/tests/test_pytables.py
@@ -1448,6 +1448,32 @@ def test_append_hierarchical(self):
             expected = df.reindex(columns=['A','B'])
             tm.assert_frame_equal(result,expected)
 
+    def test_column_multiindex(self):
+        # GH 4710
+        # recreate multi-indexes properly
+
+        index = MultiIndex.from_tuples([('A','a'), ('A','b'), ('B','a'), ('B','b')], names=['first','second'])
+        df = DataFrame(np.arange(12).reshape(3,4), columns=index)
+
+        with ensure_clean(self.path) as store:
+
+            store.put('df',df)
+            tm.assert_frame_equal(store['df'],df,check_index_type=True,check_column_type=True)
+
+            store.put('df1',df,format='table')
+            tm.assert_frame_equal(store['df1'],df,check_index_type=True,check_column_type=True)
+
+            self.assertRaises(ValueError, store.put, 'df2',df,format='table',data_columns=['A'])
+            self.assertRaises(ValueError, store.put, 'df3',df,format='table',data_columns=True)
+
+        # non_index_axes name
+        df = DataFrame(np.arange(12).reshape(3,4), columns=Index(list('ABCD'),name='foo'))
+
+        with ensure_clean(self.path) as store:
+
+            store.put('df1',df,format='table')
+            tm.assert_frame_equal(store['df1'],df,check_index_type=True,check_column_type=True)
+
     def test_pass_spec_to_storer(self):
 
         df = tm.makeDataFrame()