
    J-j                         d Z ddlmZmZ ddlZddlZddlZddlZddlm	Z	m
Z
 ddlmZ ddlZddlZddlmZ ddZddZd	 Zd
 Z G d d      Z G d de      Z G d de      Z G d de	      Z G d d      Z[[y)zTools for (lean) experimentation and collecting data.

See also cma.optimization_tools and cma.utilities.math.test*.

TODO: write some running examples as doctests.
    )divisionprint_functionN)defaultdict
namedtuple)literal_eval)warnings_and_exceptionsc                     dt        |       z   dz   d| dk(  z  z   }t        j                  d      |j                  t        j                               j	                  d      d   z   S )z.a unique timestamp up to 1/10^decimals secondsz{0:.zf}.r   z%Y-%m-%d-%Hh%Mm%Ss   )strtimestrftimeformatsplit)decimalsss     a/Users/jameslopez/projects/TradingBot25/.venv/lib/python3.12/site-packages/cma/experimentation.pyunique_time_stampr      s\    X%x1}(==A==-.HHTYY[!'',Q/1 1    c                     |3t        j                  t        |             t        j                  |       }} t        |      d|z  kD  r"| ddd   } |ddd   }t        |      d|z  kD  r"| |fS )znreturn (index, x_down) if y is None, else (x_down, y_down).

    Example: ``plot(*down_sample(mydata))``

    N   )nparangelenasarray)xylen_s      r   down_sampler      sn     	yyyQ "**Q-1
a&1t8
ccFccF a&1t8
 a4Kr   c                     t        j                  |       }t        j                  |      }t        |      r0t	        |      t        j
                  ||         z  t        |      z  S t         j                  S )a]  return the average of finite entries in `data`,

    multiplied by the ratio ``Ndata / Nfinite == len(data) /
    sum(np.isfinite(data))``.

    This measure has been called Q-measure or success performance one, sp1. It
    is computed as the average over all finite entries times the number of all
    entries divided by the number of finite entries. If all entries are finite
    this is the average value.

    Details: the multiplier operates under the assumption that non-finite
    entries contribute the same as the average finite entry and resampling can
    be applied to get a finite entry.
    )r   r   isfinitesumr   meaninf)dataaidxs      r   sp1r(   &   sR     	

4A
++a.C25c(3q6BGGAcFO#c#h.FFr   c                 <   t         j                  j                  |       s%t        j                  dj                  |              y	 t        j                  t         j                  j                  |             t        j                  | |       y# t        $ r Y "w xY w)z8copy src to dest and created dest folder(s) if necessaryz.{0} does not exist (yet), nothing to be copiedN)ospathexistswarningswarnr   makedirsdirname	Exceptionshutilcopy2)srcdests     r   	copy_filer6   9   so    77>>#FfSk	#
BGGOOD)* LLd  s   2B 	BBc                   $    e Zd ZdZddZd Zd Zy)
TimeWarnerzcontext manager to print the time spent iff it exceeds a threshold.

    Usage: ``with TimeWarner('saving'):``, the argument is used only for
    printing the message in case.
c                      || _         || _        y N)name	warn_time)selfr;   r<   s      r   __init__zTimeWarner.__init__K   s    	"r   c                 8    t        j                          | _        | S r:   )r   t0r=   s    r   	__enter__zTimeWarner.__enter__N   s    ))+r   c                     t        j                          | _        | j                  | j                  z
  | j                  kD  r=t	        dj                  | j                  | j                  | j                  z
               y y )Nz"TimeWarner: {} took {:.1f} seconds)r   t1r@   r<   printr   r;   )r=   exc_type	exc_value	tracebacks       r   __exit__zTimeWarner.__exit__Q   sW    ))+77TWWt~~-6==diiSWSZSZIZ[\ .r   Nr   )__name__
__module____qualname____doc__r>   rB   rI    r   r   r8   r8   E   s    
#]r   r8   c                   &    e Zd ZdZd Zed        Zy)ClassFromDictzMset class attributes from a `dict`, see also `cma.utilities.utils.DictClass2`c                 R    t        |      | _        |D ]  }t        | |||           y r:   )dict_dictsetattr)r=   dict_keys      r   r>   zClassFromDict.__init__X   s'    %[
CD#uSz* r   c                 @     t         fd j                  D              S )zrcollect only original attributes, check out ``.__dict__`` to see also
        the attributes later added.
        c              3   :   K   | ]  }|t        |      g  y wr:   )getattr).0rW   r=   s     r   	<genexpr>z(ClassFromDict.as_dict.<locals>.<genexpr>a   s     D#S'$,-s   )rS   rT   rA   s   `r   as_dictzClassFromDict.as_dict\   s    
 DDDDr   N)rK   rL   rM   rN   r>   propertyr]   rO   r   r   rQ   rQ   V   s     W+ E Er   rQ   c                       e Zd ZdZdej
                  ej                  ej                  fdfdZd Z	d Z
d ZddZdd	Zdd
ZddZddZd ZddZy)Resultsa  a container to communicate (changing) results via disk.

    The ``data`` attribute is a dictionary (`DataDict`) which contains all
    observed data, usually a list for each key. Any new container reads
    previously saved data (with the same name) on initialization.

    Before the data are saved, the old data are backupped under the current
    timestamp.

    Use case example::

        import cma.experimentation

        # create or load 'sweep_data' results
        res = cma.experimentation.Results('sweep_data')

        # save data after each trial
        for dim in dimensions:
            res.backup()  # in case we want fewer backups
            for ...:
                x, es = cma.fmin2(...)
                res.data[dim] += [es.result.evaluations if 'ftarget' in es.result.stop()
                                  else np.inf]
                res.save(backup=False)  # like this we can never lose data


    Details: Saving/loading of nonfinite values with `ast.literat_eval` is
    covered with the ``values_to_string`` parameters.

    Some methods handle nested dictionaries, however it seems to be safer to use
    tuples as keys in a flat dictionary or use different `Results` instances,
    e.g. by dimension and/or popsize, like::

        res = experimentation.Results('sweep_c_dim={}lam={}.format(dimension, popsize)

    In particular ``.data.clean()`` for `float` keys does (currently) not work
    with key tuples.
Nr   c                 B   |dn|| _         d| j                   dd  vr| j                   dz   | _         t        j                  j                  | j                         \  }}t        j                  j	                  |      \  }}|| _        t        j                  j                  |d|z   dz         | _        || _        t               | _
        t        d      | _        t        j                  j                  | j                         r| j                          | j                  r|d	kD  r|t        | j                        }t!        d
j#                  t%        | j                        |d	   |d   | j                   t'        | j)                         j+                                            y y y y )Nresults_dictr
   iz.pydictz._z-backup )infor   zHResults("{3}") loaded {0} data key entries ({1} ... {2}) with {4} values)filenamer*   r+   r   splitext
_extensionjoinbackup_dirname_values_to_string_valsDataDictr%   rS   	meta_datar,   loadsortedrE   r   r   r"   samplesvalues)r=   r;   values_to_stringverboser+   extkeyss          r   r>   zResults.__init__   s9    +/,DdmmBC(( MMI5DMWW]]4==1
dGG$$T*	c ggll4y1HI&6#J	277>>$--(IIKyyWq[dii( )vc$))nd1gtBx"mmS1F1F1H-IKL )y )r   c                 .    t        | j                  |      S )z/access ``Results.data`` directly from `Results`)rZ   r%   r=   r;   s     r   __getattr__zResults.__getattr__   s    tyy$''r   c                     ddl }| j                  j                  dd      d   dz   }	 |j                  |      }t        d      #  t	        j
                  d       Y yxY w)zPreturn dictionary from load json file with keys to `int` or `float`, if possibler   Nr
   r   z.jsonz?json file not loaded, this warning needs to be improved/removedz4how do we know whether to convert keys into numbers?)jsonrf   rsplitrn   r-   r.   NotImplementedErrorro   intfloat
ValueError)r=   rz   rf   resk	to_numberknews          r   
_load_jsonzResults._load_json   s]    ==''Q/2W<	))H%C ""XYY	MM[\s   A Ac                 ~   | j                   r| j                          t        | j                  d      5 }	 t	        t        |j                                     | _         	 ddd       | j                  d       | j                   j                  dd      | _        | S # t        $ r t        d        w xY w# 1 sw Y   WxY w)z<load data from file, discard current data, see also `update`rtzfPlease check whether inf or nan were used as simple value rather than in a sequence as [inf] or [nan].NTinverserm   )r%   backupopenrf   rl   r   readr1   rE   _values_to_stringpoprm   )r=   fs     r   rn   zResults.load   s    99KKM$--&!$\!&&(%;<	 ' 	t,{D9   F G '&s   B3'BB00B33B<c                     t        |      j                  }|r| j                          |D ]  }| j                  |xx   ||   z  cc<    | S )zAppend data from ``filename`` to ``self.data``.
        
        To update self with a ``data`` `dict` instead a file, call
        ``self.data.update(data)``.

        Details: assumes a flat `dict` structure.
        )r`   r%   r   )r=   rf   r   r%   rW   s        r   updatezResults.update   sE     x %%KKMCIIcNd3i'N r   c                 n   |r| j                          | j                          | j                  j                  dd      .| j                  "t        | j                        | j                  d<   	 t        j                  t        j                  j                  | j                               t        | j                  d      5 }|j                  t        | j                               ddd       | j                  j                  d       | j                  d       | S # t        $ r Y }w xY w# 1 sw Y   GxY w)zsave `self.data` to diskrm   NwtTr   )r   r   r%   getrm   reprr*   r/   r+   r0   rf   r1   r   writer   )r=   r   r   s      r   savezResults.save   s    KKM
 	 99==d+38R%)$..%9DIIk"	KK67 $--&!GGDO$ '		k"t,  		&&s   .<D  %D+	D('D(+D4c                     |r%| j                   D ]  }t        |      |k(  s|c S  |S || j                   v rt        |      S |S )z.return "correct" value, doesn't work for `nan`)rk   r   )r=   valr   val_s       r   _value_to_stringzResults._value_to_string   sJ    33:$K 4 J$---9
r   c                    | j                         D ]C  }t        |      |d   f t        t        |            D ]  }| j                  ||   |      ||<    E | j                  D ]M  }t        |      |}}|r||}}|| j                  v s%| j                  |   | j                  |<   | j                  |= O y# t        t
        f$ rD 	 t        t        |             n+#  t        j                  dj                  |             Y nxY wY w xY w)aS  replace certain values in lists with strings or vice versa.

        Values to be replaced are defined at instance creation in
        `_values_to_string_vals`.

        Details: this prevents `ast.literal_eval` to bail on, for example,
        ``repr([1, 3, 'inf'])``, as 'inf' is just a string and will not be
        evaluated.
        r   znon-sequence found as data value in {0}, which will fail when
  reading back serialized data. A simple fix is to use
  ``[val]`` instead of ``val`` or avoid values which
  `ast.literal_eval` cannot digest, like ``inf``, ``nan``, etc.N)_arraysr   ranger   	TypeError
IndexErrorr   r   r-   r.   r   rk   r%   )r=   r   r&   ir   newolds          r   r   zResults._values_to_string   s     AQ1 s1vA001w?AaD '    ..CCy#CSdii!%3		#IIcN / z* 	 a)MM\ 		s)   B44DCD&D?DDc                     || j                   }t        |t              r(g }|D ]  }|dk7  s	|| j                  ||         z  }! |S |gS )z|return a flat list of (references to) all non-dicts in data.

        Traverses recursively down into dictionaries.
        rm   )r%   
isinstancerS   r   )r=   r%   r   rW   s       r   r   zResults._arrays  sX    
 <99DdD!C+%4<<S	22C  J6Mr   c                     t         j                  j                  | j                  t	        d      | j
                  z         }t        | j                  |       || _        yz'backup saved data by making a file copyr   N)	r*   r+   ri   rj   r   rh   r6   rf   last_backupr=   r5   s     r   r   zResults.backup,  sD    ww||D//-a04??BD$--&r   c                 B   |8| j                          t        | j                        }| j                  d       nt        |      }t        | j                  | j
                  z   d      5 }|j                  |       |j                  d       ddd       y# 1 sw Y   yxY w)z0append data to backup file, no easy to read backNTr   at
)r   r   r%   r   rj   rh   r   )r=   r%   
datastringr   s       r   _backup0zResults._backup03  sx    <""$diiJ""4"0dJ$%%7>!GGJGGDM ?>>s   )#BB)TFr:   )rK   rL   rM   rN   r   r$   nanmathr>   rx   r   rn   r   r   r   r   r   r   r   rO   r   r   r`   r`   c   s_    %N %'VVRVVTXX$>L.(.*	!#F  
r   r`   c                       e Zd ZdZddZd Zeej                  fdZ	eddfdZ
d Zed	d
fdZddZddZd fdZd fdZd fdZd Zd dfdZed        ZedfdZd Zy)rl   a  A (default) dictionary like ``parameter_value: list_of_measures``,

    e.g. with float parameter or dimension as key and runtimes as value. This
    class is used under the hood in the `Results` class.

    A main functionality is the method `clean`, which joins all entries
    which have almost equal keys. This allows to have a `float` parameter
    as key.

    This class provides simple computations on this kind of data,
    like ``x, y = .xy_arrays() == sorted(keys), sp1(values)``.

    If the dictionary values are not lists, one may get rather unexpected
    results or exceptions.

    Details: this class allows to use `float` values as keys when
    `clean_key` and `set_clean` are used to access the data in the
    `dict`. Inheriting from `defaultdict` with `list` as default value,
    the syntax::

        data = DataDict()
        data[first_key] += [first_data_point]

    without initialization of the key value works perfectly fine.

    Caveat: small values are considered as the same key, even if they are
    close to zero. Either use a different comparison via the `equal`
    keyword parameter, or use ``1 / key_value`` or `log(key_value)``.

    TODO: consider `numpy.allclose` for almost equal comparison?
Nc                     t        j                  | t               |Wt        |d      r|j                  | _        t        |d      r| j                  |j                         y| j                  |       yy)zUse ``dict(dict_.data or dict_)``, and `dict_.meta_data` for
        initialization.

        Details: `dict_.meta_data` are assigned as a reference.
        Nrm   r%   )r   r>   listhasattrrm   r   r%   )r=   rV   s     r   r>   zDataDict.__init___  sZ     	T4(uk*!&uf%EJJ'E" r   c                 6    | }|D ]  }||xx   ||   z  cc<    y)z<update data lists from a `dict` of lists (and only a `dict`)NrO   )r=   rV   r%   r   s       r   r   zDataDict.updaten  s#    AGuQxG r   c           
      t    | }t        |      } ||       ||D cg c]  } |||          c}      fS c c}w )a  return two arrays ready to be plotted like ``plot(*xy_arrays)``.

        The x-array contains the sorted keys, the y-array contains the
        respectively aggregated values.

        For example to be used like::

            ``plot(*self.xy_arrays())``.

        Parameter `agg` determines the function to aggregate data values, by
        default `sp1` which is the mean corrected for missing data. To show
        dispersion, we can use ``agg=lambda x: np.percentile(x, 10)`` and ``...,
        90)``.
        )ro   )r=   aggtype_r%   ru   r   s         r   	xy_arrayszDataDict.xy_arrayst  sE     d|dT2Ts47|T235 	52s   5
Fc           
         | }d }t        |      }|D ci c]%  }|t         |||               t        ||         f' }}|rq|
 ||       |S |D cg c]  }||   	 }	}i }
t        t        |	            D ]8  } ||j	                         D ci c]  \  }}||   |k(  s|| c}}      |
|<   : |
S |S c c}w c c}w c c}}w )zWIP return a `dict` with ``(agg(data), number_of_samples)`` per key.

        For example, to get the 10%tile::

            .aggregated(lambda x: np.percentile(x, 10))

        c                     t        d | j                         D              }| D ]  }| |   d   |z  | |   d   f| |<    | S )Nc              3   &   K   | ]	  }|d      yw)r   NrO   r[   vs     r   r\   zBDataDict.aggregated.<locals>.normalize_in_place.<locals>.<genexpr>  s     2\qt\s   r   r   )minrq   )r   min_r   s      r   normalize_in_placez/DataDict.aggregated.<locals>.normalize_in_place  sJ    2SZZ\22DQT)3q6!94A Jr   )ro   r~   r   setitems)r=   r   relativebyr%   r   ru   r   r   	by_valuesres_by_r   s                r   
aggregatedzDataDict.aggregated  s     	 d|?CDt!q5T!W&DG55tDz"3' 
 -00CqQrUC	0!#i.1C 2SYY[3a[TQTUVXTY]`T`AqD[3a bDI 2
 E
 1 4bs   *B6B;C  C c                 \    | }t        |      D ci c]  }|t        ||          c}S c c}w )z1return a `dict` with the number of values per key)ro   r   )r=   r%   r   s      r   rp   zDataDict.samples  s/    )/6A3tAw<666s   )g      ?r   c                 "   | j                  |      \  }}t        j                  |      }|s||   S |dv sJ |       ||z   }|dk\  rC|t        |      k  r5||   |||   z  k  r'||z  }|dk\  r|t        |      k  r||   |||   z  k  r'|||z
     S )zAslack_index_shift can be +-1, looking to the right/left of argmin)re   r   r   )r   r   argminr   )r=   r   slackslack_index_shiftr   r   idxminr   s           r   r   zDataDict.argmin  s    ~~c"11 V9 G+>->>+&&1fSV!&	0A(A""A 1fSV!&	0A(A&&''r   c                     ddl }| }t        |      }t        t        |      |z
        D ci c]H  }||   |||z      ft	        |j
                  j                  |||      ||||z         |d      d         J c}S c c}w )zIreturn p-values of the `mannwhitneyu` test of entries adjacent with `gap`r   N	two-sidedmethodalternativer   )scipy.statsro   r   r   r~   statsmannwhitneyu)r=   gapr   scipyr%   ru   r   s          r   testszDataDict.tests  s    d|
 s4y3/	1 0A	 a$qu+&KK,,T!WtD3K'8%; - @@AC)D D 0	1 	1 1s   AA;c           	      T   ddl }| }t        |      }||vrt        dj                  ||            |B|j	                  |      dz   }|t        |      k\  rt        dj                  ||            ||   }||ft        |j                  j                  ||   ||   |d      d         iS )z)return p-value of the `mannwhitneyu` testr   Nzkey {} not in data keys {}r   z!key {} is the last key in data {}r   r   )	r   ro   r   r   indexr   r~   r   r   )r=   rW   key2r   r   r%   ru   r'   s           r   testzDataDict.test  s    d|d?9@@dKLL<**S/A%Cc$i !D!K!KCQU!VWW9DdUKK,,S	4:%; - @@ACD E 	Er   c                 .    | dz
  |cxk  xr | dz   k  S c S Ngư>rO   r   r   s     r   <lambda>zDataDict.<lambda>  s    q4x!'>a$h'>r   c                 r    t        | j                               D ]  }|| vr| j                  ||        | S )z+merge keys which have almost the same value)equal)r   ru   	clean_key)r=   r   rW   s      r   cleanzDataDict.clean  s8    		$C$NN3eN, % r   c                 .    | dz
  |cxk  xr | dz   k  S c S r   rO   r   s     r   r   zDataDict.<lambda>      D10Gq4x0Gr   c                     | j                  dd      }| j                  ||      |urA| j                  ||      }||k7  sJ | |xx   | |   z  cc<   | |= | j                  ||      |urA||| d<   |S )zset similar key values all to be `key`, return `key`.

        Use method `set_clean` to access and change the clean-key
        dictionary *value* more conveniently.
        rm   N)r   	_near_key)r=   rW   r   rm   r   s        r   r   zDataDict.clean_key  s     HH[$/	nnS%(3sE*A8O8Ia IQ	 nnS%(3
   )D
r   c                 .    | dz
  |cxk  xr | dz   k  S c S r   rO   r   s     r   r   zDataDict.<lambda>  s    q4x!/Fa$h/Fr   c                     || vrg nt        | |         }|g}| j                  |||      |ur7| j                  |||      }||gz  }|| |   z  }| j                  |||      |ur7|S )zget the merged values list of all nearby keys.

        Caveat: the returned value is a new list

        :See also: `clean`, `set_clean`.
        )r   r   )r=   rW   r   r   	done_keysr   s         r   get_nearzDataDict.get_near  s~     tObd3iE	nnS%33>sE95A!I47NC nnS%33> 
r   c                 .    | j                  |       | |   S )a  join all entries with similar `key` and return the new value,
        a joined list of all respective values.

        This is the same as `clean_key` which however returns the new key, not
        the values.

        Example::

            data.set_clean(key) += [new_data_point]

            # same as
            data[data.clean_key(key)] += [new_data_point]

            # or more explicite, however with a different order of the data
            data[key] += [new_data_point]
            data.clean_key(key)  # joins data, however in the "wrong" order

            # similar as
            data[key] += [new_data_point]
            data.clean()  # cleans *all* keys

        )r   )r=   rW   s     r   	set_cleanzDataDict.set_clean  s    . 	sCyr   c                 .    | dz
  |cxk  xr | dz   k  S c S r   rO   r   s     r   r   zDataDict.<lambda>  r   r   c                     |g }t        | j                               D ]  }	  |||      r||k7  r||vr|c S  |S # t        $ r Y )w xY w)zRreturn a key in self which is ``equal`` to ``key`` and otherwise ``key``.
        )ro   ru   r   )r=   rW   r   excluder   s        r   r   zDataDict._near_key  s_     ?G		$AC=Q#X!72BH % 
  s   <	AAc                    t        |       D cg c]  }t        j                  |      s| }}t        j                  |D cg c]#  }t	        t        j
                  | |               % c}      }t        j                  |D cg c]  }t        | |          c}      }	 t        dg d      } |t        j                  |      ||||z        }|S c c}w c c}w c c}w #  t        t        j                  |      ||||z  d      }Y |S xY w)zreturn a class instance with attributes `x` (i.e. keys), `n`,
        `nsucc`, and `rate` as arrays.

        TODO: consider using cma.utilities.utils.DictClass2 instead namedtuple?
        	Successes)r   nsuccnrate)	ro   r   isscalarr   r"   r!   r   r   rQ   )r=   r   ru   r   r   r   r   s          r   	successeszDataDict.successes  s     "$<:<a2;;q><:

tDt!CDG 45tDEJJd3dDGd34
	";#>@IBJJt,eQ	BC 
 ;D3
	jj&C 
s"   CC(CC!/C! !(Dd   c                     t         )z-TODO:review percentile based on bootstrapping)r|   ro   r   r   r   randomrandintr   append
percentiler   )r=   prctiler   rp   r   ru   r   	bstrappedr   r'   r%   s              r   r   zDataDict.percentile6      !!r   c                 *    t        t        |             S r:   )r   rS   rA   s    r   __repr__zDataDict.__repr__D  s    DJr   r:   )r   auto)Nr  )rK   rL   rM   rN   r>   r   r(   r   r   r   r   rp   r   r   r   r   r   r   r   r   r^   r   r   r  rO   r   r   rl   rl   ?  s    >#   rzz 5( !5T 87
 C2 
(	1E" ?  $H   #G 4 $H  , '*3 1 r   rl   c                       e Zd ZdZddZd Zed        ZddZddZ	d Z
d Zd	 Zi i fd
Zi i fdZd Zd ZddZed        Zd Zd Zy)ResultsPandasa
  WIP ad hoc code for writing (changing) results in a `pandas.DataFrame` and to disk,

    where "results" refers to end-results of experiment repetitions rather
    than the traces of single runs.

    Caveat: this is an ad hoc implementation, some interfaces may be incomplete,
    interface details are still versatile and in flux, some recent changes may be
    faulty.

    Main features, similar to `Results`, are

    * Intermediate savings of results which consequently can be loaded from a
      different shell while the experiment is running.
    * Backup under the current timestamp before each saving into a
      ``'backups-name'`` folder (optional but default).
    * Similar float values can be "equalized" for correct data aggregation, see
      the `check_close_values` and `equalize_close_values` methods. This uses
      `np.isclose` which considers 1e-8 to be close to 1e-9 and 1+1e-5 close to
      1+1e-6.

    Compared to `Results`, this class is useful to store more information for
    each run, like the termination condition or a final condition number or
    constraints violations or meta parameter information. (In contrast, with
    `Results` the workaround for catching the final condition would writing a
    nonfinite entry when the target was not reached.)

    A guiding code example::

        import cma.experimentation

        res = cma.experimentation.ResultsPandas('some-name')  # reloads data to append/continue
        for dim in dimensions:
            es = cma.CMA(dim * [2], 1, {'verbose': -9})
            # a tracker in case we want to track, say, a minimum over the trace as result
            es.optimize(cma.ff.rosen, callback=my_result_tracker)
            if es.opts.get('verbose') == -9:  # do not save data while testing the setup
                res.append([es.N, es.popsize,
                            es.result.evaluations,
                            1 if 'ftarget' in es.stop() else 0,
                            repr(es.stop),
                            es.condition_number,
                            my_result_tracker.my_val_of_interest],
                            colums=['dimension', 'popsize',
                                    'evaluations',
                                    'targethit',
                                    'stopcondition',
                                    'conditionnumber',
                                    'myvalue'])
                res.save()

        # load the data:
        res = cma.experimentation.ResultsPandas('some-name')
        print(res.summary)  # summary statistics of columns in a dict
        res.df  # the pandas data frame

    Notes: ``self.df.drop(...)`` could be used to return a data frame with some
    entried dropped.
c                    	 ddl }|| _        || _        |dk(  r	 ddl}t        j                  j                  | j                        \  }}t        j                  j                  |d|z         | _        t        j                  j                  | j                  | j
                  z         r| j                          n|j                         | _        t        j                  dt         j"                  	       y# t        $ r t        j                  d        w xY w# t        $ r  t        j                  d       d| _        Y !w xY w)
zload data when `filepathname` exists.

        ``format='.csv'`` is human readible, however ``'.feather'`` is much more
        performant, ``'.parquet'`` should work too.
        r   Nz<Please 'pip install pandas' to use the `ResultsPandas` class.featherzOto use the .feather format: 'pip install pyarrow'
using .csv for the time being.csvzbackups-zEThe ResultsPandas class is versatile and has not be thoroughly tested)category)pandasImportErrorr-   r.   r;   rh   pyarrowr*   r+   r   ri   rj   r,   rn   	DataFramedf_warnings_and_exceptionsNeverTestedWarning)r=   filepathnamer   pdr  pr   s          r   r>   zResultsPandas.__init__  s    	
 !	  Z)
 ww}}TYY'1 ggll1j1n=77>>$))doo56IIKllnDG]7JJ	L+  	MMXY	  ) > ?"()s   C4 D 4 D%E ?E c                 .    t        | j                  |      S )z=access the `DataFrame` ``Results.df`` directly from `Results`)rZ   r  rw   s     r   rx   zResultsPandas.__getattr__  s    tww%%r   c           	         | }i }|j                   j                  D ]  }|j                   |   j                         j                         |j                   |   j                         j	                         }}t        |t        t        j                  f      r7t        |t        t        j                  f      rt        |      t        |      }}n	 t        |      t        |      }}	 t        |j                   |   j                               }|t        t        |j                   |               ||fg||<    |S # t        $ r Y `w xY w# t        $ r+ t        t        d |j                   |   D                    }Y pw xY w)znreturn number of finite entries, number of different values, and

        (min, max) for each column.
        c              3   $   K   | ]  }|d v 
 yw))r   rc   NrO   r   s     r   r\   z(ResultsPandas.summary.<locals>.<genexpr>  s     !M1!;"6s   )r  columnsdropnar   maxr   r}   r   integerr~   r1   r   r"   r   )r=   r   rp   r;   r   max_n_valids          r   summaryzResultsPandas.summary  s@    FFNND,,.224cffTl6I6I6K6O6O6Q$D$bjj 12z$bjjHY7Z YD	d!&teDk$DOcffTl1134 % SVVD\!23#Tl,GDM #   !   Oc!Mt!MMNOs$   D8$&E8	EE1E;:E;c                 $   |xr | j                          	 t        j                  t        j                  j	                  | j
                  | j                  z                t        d|      5  | j                  dk(  rG|j                  dd        | j                  j                  | j
                  | j                  z   fi | n| j                  dk(  r5 | j                  j                  | j
                  | j                  z   fi | ng| j                  dk7  r.t        j                  dj                  | j                                | j                  j                   | j
                  dz   fi | ddd       y# t        $ r Y #w xY w# 1 sw Y   yxY w)	a  save data, warn when saving takes more than `time_s` seconds.

        `kwargs` are passed to the saving method of the `pandas` data frame.

        Details: `save` is in essence just a shortcut for::

            self.backup()
            self.df.to_feather(self.name + self.extension)  # assuming .extension == '.feather'

        TODO: generalize by passing the `DataFrame` method name for saving, like
        ``.save('to_feather')``? Annoyingly, pandas does not add a proper
        extension by default.
        r   r  r   Fz.parquetr
  z8'{0}' extension not recognized, saving in feather formatN)r   r*   r/   r+   r0   r;   rh   r1   r8   
setdefaultr  to_csv
to_parquetr-   r.   r   
to_feather)r=   r   time_skwargss       r   r   zResultsPandas.save  s*    	 4;;=	KK		DOO(CDE '&(!!'51tyy4??:EfEJ."""499t#>I&I??j0MM"\#)6$//#:<"""499z#9DVD ('  		''s   A	E6 +DF6	FFFc                    dd l }t        d      5  | j                  dk(  r.|j                  | j                  | j                  z         | _        nj| j                  dk(  r.|j                  | j                  | j                  z         | _        n-|j                  | j                  | j                  z         | _        d d d        | S # 1 sw Y   | S xY w)Nr   zResultsPandas.loadr  z.parqet)	r  r8   rh   read_csvr;   r  
_extentionread_parquetread_feather)r=   polishr  s      r   rn   zResultsPandas.load  s    ,-&(++dii$//&ABI-//$))doo*EF//$))doo*EF .  . s   B(CCc                     | j                   }t        t        ||               }t        t	        j
                  |d d |dd              D ]/  \  }}|s	||   |j                  t        ||      ||dz      k(  |f<   1 y )Nre   r   )r  ro   r   	enumerater   iscloselocrZ   )r=   columnr  r   r   ts         r   equalize_close_valuesz#ResultsPandas.equalize_close_values  su    WW3r&z?#bjj3B1278DAq@A!wr6*a!f4f<= 9r   c                    | j                   j                  D cg c]  }d|v  c}| j                   d<   | j                  j                  dg      j                  j                         }| j                   j                  D cg c]<  }||v r&t        | j                   j                  ||      |      nt        j                  > c}| j                   d|z   <   yc c}w c c}w )za quick hackcallbacksuccess	dimensionbest_N)r  stopdf_succgroupbyevalsr   r8  rZ   r1  r   r   )r=   r2  r   r'   dims        r   _polishzResultsPandas._polish  s    7;ww||D|!jAo|D	ll""K=177>>@.2gg.?.?%A.?s PSVYzWTWW[[S-BF%K_a_e_e%e.?%A& ! E%As   C ACc                     t        t        t        | j                  |                  }t	        t        j                  |d d |dd              r&t        j                  dj                  ||             y y )Nre   r   zdFound ambiguous double values in column {0} in {1}.Call .equalize_close_values() and .save() to fix.)
ro   r   rZ   r  anyr   r0  r-   r.   r   )r=   r2  r   s      r   check_close_valuesz ResultsPandas.check_close_values  s^    3wtww/01rzz!CR&!AB%()MM N!6&!,. *r   c                 .    | j                  |g|||       y)zappend a single data rowN)extend)r=   r%   r  	kwargs_dfkwargs_concats        r   r   zResultsPandas.append   s     	TFGY>r   c                     ddl }|j                  dd        |j                  | j                   |j                  j
                  |fd|i|gfi || _        y)z0extend frame by data which is a sequence of rowsr   Nignore_indexTr  )r  r"  concatr  r  from_records)r=   r%   r  rE  rF  r  s         r   rD  zResultsPandas.extend  s[      6"))TWW%BLL%%dIGIyIK ]N[]r   c                     t         )z)call ``self.df.drop`` and reassign result)r|   r  drop)r=   argsr'  s      r   rL  zResultsPandas.drop  r  r   c                 >    | j                   j                  dd       y)z;caveat: the original index is lost which may be undesirableT)rL  inplaceN)r  reset_indexrA   s    r   rP  zResultsPandas.reset_index  s    t4r   c                     | j                   i }}|rAt        t        ||               D ]%  }t        t        |||   |k(     |               ||<   ' |S t        t        ||               }|S )z7a sorted list of values in the column of name `column`.)r  ro   r   )r=   r2  r   r  r   r8  s         r   column_valueszResultsPandas.column_values  so    ''2C#C2K0	!'Br"v/B,CF,K(L!MI 1 
 RZ)C
r   c                      j                   j                         D cg c]  }d|j                  vs|j                   }} fd} j                   j                         D cg c]  } |||      s|j                   c}S c c}w c c}w )z>TODO: revise such that we have a boolean failed_setting columnr6  c                     |D ]c  }| j                   t        |       fj                  j                  |   j                   t        j                  j                  |         fk(  sc y y)NTF)r8  rZ   r  r1  )dfailed_indicesr   r2  r=   s      r   failed_parameterz7ResultsPandas.failure_indices.<locals>.failed_parameter  s`    #KKF!34Q9Q9QSZ[_[b[b[f[fgh[ikqSr8ss $ r   )r  
itertuplesr:  Index)r=   r2  rU  failedrW  s   ``   r   failure_indiceszResultsPandas.failure_indices  s|     $(77#5#5#7T#7a:QVV;S!''#7T	
 "&!3!3!5U!5A9I!V9T!5UU U Vs   BB%B
4B
c                 p   | j                   }t        dj                  t        |      t        | j                        | j                        dj                  |t        t        j                  t        j                  t        j                  t        t        ||                                                    y)zdo not use `iterrows` but `itertuples`
        https://stackoverflow.com/questions/16476924/how-to-iterate-over-rows-in-a-dataframe-in-pandas
        z.{0} data, {1} failures, failure_indices = {2}
zmin {0} fraction = {1:.4}N)r  rE   r   r   r[  r   r   expdifflogro   r   )r=   r2  r  s      r   print_checkszResultsPandas.print_checks%  s     WW?FFs2wPSTXThThPikokk  A)00RVVBGGBFFSYZ]^`ag^hZiSjLkDl=m9no	r   c                     t         j                  j                  | j                  t	        d      | j
                  z         }t        | j                  | j
                  z   |       || _        yr   )	r*   r+   ri   rj   r   rh   r6   r;   r   r   s     r   r   zResultsPandas.backup.  sM    ww||D//-a04??BD$))doo-t4r   N)r  )Tr   r   )r8  )rK   rL   rM   rN   r>   rx   r^   r   r   rn   r4  r?  rB  r   rD  rL  rP  rR  r[  r`  r   rO   r   r   r  r  G  s    9tL>&  2E>EA. /1 ? /1 ]05 V V r   r  rJ   )Ni  )rN   
__future__r   r   r-   r*   r2   r   collectionsr   r   astr   r   numpyr   cmar   r  r   r   r(   r6   r8   objectrQ   r`   rl   r  rO   r   r   <module>rh     s    0  	   /    C1G&
] ]"EF EZf ZxF { F Pl  l \ nr   