-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmathgraph.py
More file actions
5892 lines (4760 loc) · 250 KB
/
Copy pathmathgraph.py
File metadata and controls
5892 lines (4760 loc) · 250 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python
"""
"""
from __future__ import division # Python3-style integer division 5/2=2.5, not 3
if 0 and 'not while trying epd': # This was in place 'til May 2012: upgrade to Ubuntu 12.04
from IPython.Debugger import Tracer; debug_here = Tracer()
try:
import rpy2.robjects as robjects
except:
print(' ((cpblUtils: rpy2 not available on this machine))')
import os,sys
import re
from copy import deepcopy
#import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl # Not yet used, may 2010
# WHAT!? matplotlib.pyplot is not the same as pylab?
#import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import pandas as pd
#from pylab import *
# Why not define these lobally? June 2010
from pylab import figure,plot,ylim,xlim,setp,clf,array,isnan,nan,find,text,isfinite,xlabel,ylabel,title,arange,subplot,gca
NaN=nan
import scipy as sci
from .color import cifar_colors
from .figure_to_inverse_video import figureToInverseVideo, figureToGrayscale
"""
Solutions for bounding box / whitespace in output figures:
plt.subplots_adjust(left = 0.05,right=1-tiny,bottom=0.1,top=1-tiny) # BRILLIANT!!! USE subplot_tool() to find values!
"""
#print 'fyi: __See figureFontSetup() for plot settings (cpblUtilMathGraph)'
#####################################################################################
def dfOverplotLinFit(df,xv,yv,aweights=None, label=None,ax=None,ci=True,
fill_color = '#888888', fill_alpha=0.4,
**kwargs): # This is for a bivariate relationship just now.
""" 2015June: overplot a linear fit (no se shown now) and return b, se for bivariate DataFrame
You can pass a label string which refers to some of the fit parameters: ['beta','2se','r2'] as floats. For example:
label=' OLS '+r' ($\beta$=%(beta).2g$\pm$%(2se).2g)'
ci=False suppresses the confidence interval shadow band
To do:
This should (that this is not obvious/trivial is sad for Python)
- show the envelope of cI
- allow for confidence weights for each datapoint
- or allow for sampling weights for each datapoint
Example: (strange example; this is the case when I've already done the OLS elsewhere and know the pvalue)
ps = chooseSFormat(pvalue+1e-5, lowCutoff=.0001)
dfOverplotLinFit(df, xv, yv, fill_alpha=.05, ax=ax, label='$p$'+'='*('<' not in ps)+ps)
plt.legend(title='this one')
Better example:
label = 'R$^2$$_a$={r2a:.2f}'
See code below; you can use substitution strings in the label value, to use results from the regression. This label is associated with the line. Use legend() to show it.
"""
if ax is None:
ax=plt.gca()
if 0: # Here's one quick method!!
import seaborn as sns; sns.set(color_codes=True)
#>>> tips = sns.load_dataset("tips")
g = sns.lmplot(xv,yv,data=df, markers=None, )
import statsmodels.formula.api as smf
import statsmodels.regression.linear_model as lm
import statsmodels.api as sm
# WTH? which of these three (one above, two below) are we to use?
import pandas.stats.api as pds
import statsmodels.regression.linear_model as olsm
"""import statsmodels.formula.api as sm
>>> df = pd.DataFrame({"A": [10,20,30,40,50], "B": [20, 30, 10, 40, 50], "C": [32, 234, 23, 23, 42523]})
>>> result = sm.ols(formula="A ~ B + C", data=df).fit()
"""
###df = pd.DataFrame({"A": [10,20,30,40,50], "B": [20, 30, 10, 40, 50], "C": [32, 234, 23, 23, 42523]})
weights = 1 if aweights is None else df[aweights] # Careful!! Do I want 1/weights or weights?!
if 1: # I literally fixed this twice, once on laptop, and once on server. Booh. Here is the server version: 2018-05
newdf = df[[xv, yv]+ [aweights]*(aweights is not None)].dropna()
Y, X = newdf[yv].astype(float).values, newdf[xv].astype(float).values
X = olsm.add_constant(X)
res = olsm.OLS(Y, X).fit()
print res.summary()
b0, beta,se, yhat = res.params[0], res.params[1], res.bse[0], res.predict()
mean_x = newdf[xv].mean()
n = len(newdf)
dof = n - res.df_model - 1
#res = olsm.OLS( df[[xv]], df[yv], weights=weights).fit() # pds.ols(y=df[yv], x=df[[xv]], weights=weights)
#beta,se= res.beta[xv], res.std_err[xv]
from pylab import plot
if label is not None:
#label=label%{'beta':beta,'2se':1.96*se, 'r2':res.rsquared, 'r2a': res.rsquared_adj}
label=label.format(**{'beta':beta,'2se':1.96*se, 'r2':res.rsquared, 'r2a': res.rsquared_adj})
#ax.plot(df[xv],res.y_predict,label=label,**kwargs)
ax.plot(newdf[xv], yhat,label=label,**kwargs)
#import statsmodels.api as sm
#x = sm.add_constant(x) # constant intercept term
# Model: y ~ x + c
#model = sm.OLS(y, x)
#fitted = model.fit()
x_pred = np.linspace(newdf[xv].min(), newdf[xv].max(), 50)
#y_pred = fitted.predict(x_pred2)
y_pred= b0 + x_pred*beta
#ax.plot(x_pred, y_pred, '-', color='darkorchid', linewidth=2)
from scipy import stats
t = stats.t.ppf(1-0.025, df=dof)
s_err = np.sum(np.power(res.resid, 2))
conf = t * np.sqrt((s_err/(n-2))*(1.0/n + (np.power((x_pred-mean_x),2) /
((np.sum(np.power(x_pred,2))) - n*(np.power(mean_x,2))))))
upper = y_pred + abs(conf)
lower = y_pred - abs(conf)
if ci in [True]:
ax.fill_between(x_pred, lower, upper, color='#888888', alpha=0.4)
if 0: # And here is the laptop version 2018-05
if 0: res = pds.ols(y=df[yv], x=df[[xv]], weights=weights)
y,x = df[yv].values, df[xv].values
x= lm.add_constant(x)
olsm = lm.OLS(y, x, weights=weights)
results = olsm.fit()
#import statsmodels.regression.linear_model as olsm
b_intercept, b_beta= results.params
t_intercept, t_beta = results.tvalues
p_intercept, p_beta = results.pvalues
se_intercept, se_beta = results.HC2_se
beta, se = b_beta, se_beta
yhat = results.predict()
#beta,se= res.beta[xv], res.std_err[xv]
from pylab import plot
if label is not None:
label=label%{'beta':beta,'2se':1.96*se, 'r2':results.rsquared}
ax.plot(df[xv], yhat, label=label,**kwargs)
if 1:
#import statsmodels.api as sm
#x = sm.add_constant(x) # constant intercept term
# Model: y ~ x + c
#model = sm.OLS(y, x)
#fitted = model.fit()
x_pred = np.linspace(df[xv].min(), df[xv].max(), 50)
#y_pred = fitted.predict(x_pred2)
y_pred= b_intercept + x_pred* b_beta
#ax.plot(x_pred, y_pred, '-', color='darkorchid', linewidth=2)
mean_x = df[xv].mean()
n = len(df)
dof = n - results.df_model - 1
from scipy import stats
t = stats.t.ppf(1-0.025, df=dof)
s_err = np.sum(np.power(results.resid, 2))
conf = t * np.sqrt((s_err/(n-2))*(1.0/n + (np.power((x_pred-mean_x),2) /
((np.sum(np.power(x_pred,2))) - n*(np.power(mean_x,2))))))
upper = y_pred + abs(conf)
lower = y_pred - abs(conf)
if ci in [True]:
ax.fill_between(x_pred, lower, upper, facecolor= fill_color, alpha= fill_alpha, edgecolor = 'None')
if 0: # Last part: show 95% confidence interval of predicted values (as opposed to regression line)
x_pred2 = sm.add_constant(x_pred)
from statsmodels.sandbox.regression.predstd import wls_prediction_std
sdev, lower, upper = wls_prediction_std(fitted, exog=x_pred2, alpha=0.05)
ax.fill_between(x_pred, lower, upper, color='#888888', alpha=0.1)
if 0:
from statsmodels.sandbox.regression.predstd import wls_prediction_std
#measurements genre
nmuestra = 100
x = np.linspace(0, 10, nmuestra)
e = np.random.normal(size=nmuestra)
y = 1 + 0.5*x + 2*e
X = sm.add_constant(x)
re = sm.OLS(y, X).fit()
print re.summary() #print the result type Stata
prstd, iv_l, iv_u = wls_prediction_std(re)
from statsmodels.sandbox.regression.predstd import wls_prediction_std
prstd, iv_l, iv_u = wls_prediction_std(res)
ax.plot(x, iv_u, color+'--')
ax.plot(x, iv_l, color+'--')
return(beta,se)
"""
def dfOverplotLinFit(df,xv,yv):
# this uses the statsmodels formula API (same results
# import formula api as alias smf
import statsmodels.formula.api as smf
# formula: response ~ predictors
est = smf.ols(formula='Units ~ lastqu', data=df2).fit()
est.summary()
fig = plt.figure(figsize=(12,8))
fig=sm.graphics.plot_regress_exog(est,'lastqu',fig=fig)
"""
def overplotLinFit(x,y,format=None,color=None,xscalelog=False):
"""
March 2010: upgrading it so that the fit line doesn't extrapolate beyond the x,y range of data.
"""
from pylab import log
if format==None:
format='k--'
if color==None:
color=cifar_colors['grey']
from pylab import plot,array,xlim,any
if all(isnan(x)):
return()
if 0: # Use quick method, no standard errors
from rpy2 import r
ls_fit = r.lsfit(x,y)
gradient = ls_fit['coefficients']['X']
yintercept= ls_fit['coefficients']['Intercept']
xr=array([max(min(x),min(xlim())),min(max(x),max(xlim()))])
yr=[yintercept+gradient*xr[0],yintercept+gradient*xr[1]]
plot(xr,yr,format,color=color)
return(gradient)
if xscalelog:
x=np.log(x)
if 1: # FIT A LINE TO THE NON-NAN ELEMENTS, OVERPLOT IT
from scipy import stats as stats
from pylab import where,isfinite,logical_and
ii=where(logical_and(isfinite(x),isfinite(y)))
( gradient, yintercept, rrr, twoTailedProb, stderr)=stats.linregress(x[ii],y[ii])
s=stderr
else:
import rpy2.robjects as robjects
rpy = robjects.r
#import rpy2 as rpy
rpy.set_default_mode(rpy.NO_CONVERSION)
if xscalelog:
x=np.log(x)
linear_model = rpy.r.lm(rpy.r("y ~ x"), data = rpy.r.data_frame(x=x, y=y))
rpy.set_default_mode(rpy.BASIC_CONVERSION)
gradient=linear_model.as_py()['coefficients']['x']
yintercept=linear_model.as_py()['coefficients']['(Intercept)']
s=rpy.r.summary(linear_model)['sigma']
print ' b: ',gradient
#{'x': 5.3935773611970212, '(Intercept)': -16.281127993087839}
print ' sigma: ',s
#{'terms': <Robj object at 0x0089E240>, 'fstatistic': {'dendf': 2.0, 'value': 2.2088097871524752, 'numdf': 1.0}, 'aliased': {'x': False, '(Intercept)': False}, 'df': [2, 2, 2], 'call': <Robj object at 0x0089E340>, 'residuals': {'1': -9.3064376809571137, '3': -6.9622553363545983, '2': 6.3744808050079511, '4': 9.8942122123037599}, 'adj.r.squared': 0.28720941270437206, 'cov.unscaled': array([[ 2.1286381 , -0.42527178], [-0.42527178, 0.09626979]]), 'r.squared': 0.524806275136248, 'sigma': 11.696414461570097, 'coefficients': array([[-16.28112799, 17.06489672, -0.95407129, 0.44073772], [ 5.39357736, 3.62909012, 1.48620651, 0.27556486]])}
"""
import rpy2.robjects as robjects
r = robjects.r
# Create the data by passing a Python list to RPy2, which interprets as an R vector
ctl = robjects.FloatVector(x)
trt = robjects.FloatVector(y)
group = r.gl(2, 10, 20, labels = ["Ctl","Trt"])
weight = ctl + trt
# RPy2 uses Python dictionary types to index data
robjects.globalEnv["weight"] = weight
robjects.globalEnv["group"] = group
# Run the models
lm_D9 = r.lm("weight ~ group")
print(r.anova(lm_D9))
lm_D90 = r.lm("weight ~ group - 1")
print(r.summary(lm_D90))
"""
# Limit extent of the plotted line to the x-extent of non-nan points
xxx=[xx for ix,xx in enumerate(x) if isfinite(xx) and isfinite(y[ix])]
lxlim=xlim()
if xscalelog:
lxlim=log(lxlim)
xr=array([max(min(xxx),min(lxlim)),min(max(xxx),max(lxlim))])
yr=[yintercept+gradient*xr[0],yintercept+gradient*xr[1]]
if xscalelog:
xr=np.exp(xr)
aline=plot(xr,yr,format,color=color)
# Display fit?
"""
Here I should add a transAnnotation with the fit info.
"""
return(aline,gradient,s)
# The following taken from skipper of pystatsmodels... because it allows for errorbars. so incorporate it into mine, above.
def linfit2D(y, x=None, y_unc=None):
import numpy as np
import scipy.special as ss
"""
Fits a line to 2D data, optionally with errors in y.
The method is robust to roundoff error.
Parameters
----------
y: ndarray
Ordinates, any shape.
x: ndarray
Abcissas, same shape. Defaults to np.indices(y.length))
y_unc: ndarray
Uncertainties in y. If scalar or 1-element array, applied
uniformly to all y values. [NOT IMPLEMENTED YET!] Must be
positive.
Returns
-------
a: scalar
0 Fitted intercept
b: scalar
1 Fitted slope
a_unc: scalar
2 Uncertainty of fitted intercept
b_unc: scalar
3 Uncertainty of fitted slope
chisq: scalar
4 Chi-squared
prob: scalar
5 Probability of finding worse Chi-squared for this model with
these uncertainties.
covar: ndarray
6 Covariance matrix: [[a_unc**2, covar_ab],
[covar_ab, b_unc**2]]
yfit: ndarray
7 Model array calculated for our abcissas
Notes
-----
If prob > 0.1, you can believe the fit. If prob > 0.001 and the
errors are not Gaussian, you could believe the fit. Otherwise
do not believe it.
See Also
--------
Press, et al., Numerical Recipes in C, 2nd ed, section 15.2,
or any standard data analysis text.
Examples
--------
>>> import linfit
>>> a = 1.
>>> b = 2.
>>> nx = 10
>>> x = np.arange(10, dtype='float')
>>> y = a + b * x
>>> y_unc = numpy.ones(nx)
>>> y[::2] += 1
>>> y[1::2] -= 1
>>> a, b, sa, sb, chisq, prob, covar, yfit = linfit.linfit(y, x, y_unc)
>>> print(a, b, sa, sb, chisq, prob, covar, yfit)
(1.272727272727272, 1.9393939393939394, 0.58775381364525869, 0.11009637651263605, 9.6969696969696937, 0.28694204178663996, array([[ 0.34545455, -0.05454545],
[-0.05454545, 0.01212121]]), array([ 1.27272727, 3.21212121, 5.15151515, 7.09090909,
9.03030303, 10.96969697, 12.90909091, 14.84848485,
16.78787879, 18.72727273]))
Revisons
--------
2007-09-23 0.1 jh@... Initial version
2007-09-25 0.2 jh@... Fixed bug reported by Kevin Stevenson.
2008-10-09 0.3 jh@... Fixed doc bug.
2009-10-01 0.4 jh@... Updated docstring, imports.
"""
# standardize and test inputs
if x == None:
x = np.indices(y.length, dtype=y.dtype)
x.shape = y.shape
if y_unc == None:
y_unc = np.ones(y.shape, dtype=y.dtype)
# NR Eq. 15.2.4
ryu2 = 1. / y_unc**2
S = np.sum(1. * ryu2)
Sx = np.sum(x * ryu2)
Sy = np.sum(y * ryu2)
# Sxx = np.sum(x**2 * ryu2) # not used in the robust method
# Sxy = np.sum(x * y * ryu2) # not used in the robust method
# NR Eq. 15.2.15 - 15.2.18 (i.e., the robust method)
t = 1. / y_unc * (x - Sx / S)
Stt = np.sum(t**2)
b = 1. / Stt * np.sum(t * y / y_unc)
a = (Sy - Sx * b) / S
covab = -Sx / (S * Stt) # NR Eq. 15.2.21
sa = np.sqrt(1. / S * (1. - Sx * covab)) # NR Eq. 15.2.19
sb = np.sqrt(1. / Stt) # NR Eq. 15.2.20
rab = covab / (sa * sb) # NR Eq. 15.2.22
covar = np.array([[sa**2, covab],
[covab, sb**2]])
yfit = a + b * x
chisq = np.sum( ((y - yfit) / y_unc)**2 )
prob = 1. - ss.gammainc( (y.size - 2.) / 2., chisq / 2.)
return a, b, sa, sb, chisq, prob, covar, yfit
##############################################################################
##############################################################################
#
def cpblOLS(y,xs,betacoefs=False,rhsOnly=None):
##########################################################################
##########################################################################
"""
I want to have my OLS routine:
- take a list of vectors
- get heteroskedasticity-robust errors back
- have option to get standardized beta coeffcients rathr than raw b
- ignore NaNs
- take sample weights. (WLS)
So xs can/should be a dict of named vectors.
Actually, here's my preferred calling format: yname,xnames, dataDict
but dataDict should be able to be a list of dicts or a dict of named vectors.
sep 2010: this failed, ie is incompelte, since I can't see how to do weights with it.
"""
import numpy as np
import scikits.statsmodels as sm
# Not yet flexi calling format:
assert isinstance(y,str)
#assert isinstance(x,list)
#assert isinstance(x[0],str)
assert isinstance(xs,dict)
dataDict=xs
Y=dataDict.pop(y) # Remove y from the data to be used for x.
if not rhsOnly:
rhsOnly=dataDict.keys()#list(set(x.keys())-set([y]))
# get data
if betacoefs:
X = sm.tools.add_constant(np.column_stack([(xs[kk]-np.mean(finiteValues(xs[kk])))/np.std(finiteValues(xs[kk])) for kk in rhsOnly]))
else:
X = sm.tools.add_constant(np.column_stack([xs[kk] for kk in rhsOnly]))
#beta = np.array([1, 0.1, 10])
#y = np.dot(X, beta) + np.random.normal(size=nsample)
# run the regression
results = sm.OLS(Y, X,weights=Y).fit()
foiu
# look at the results
#print results.summary()
#and look at `dir(results)` to see some of the results
#that are available
return(bs,rses)
##############################################################################
##############################################################################
#
def _obselete_use_savefigall_exportPythonFigToFormats(fileName):
##########################################################################
##########################################################################
from pylab import savefig
for ff in ['pdf','png','eps']:
# Should still develop this to get rid of borders!
savefig(fileName+'.'+ff,format=ff,transparent=True)
def resizeSVG_or_PDF_with_inkscape(infile,outfile=None,use_xvfb=False):
"""
- How to rescale a vector PDF or SVG to have specific dimensions in spatial units? Key: you cannot resize both content and file at the same time. So in inkscape, it's two steps. Select all content, change its size (rescale it), and then rescale the docuemtn to its content in document proporties.
And it seems you can't automate this from the command line in inkscape. See: https://answers.launchpad.net/inkscape/+question/143221
"""
def tightBoundingBoxPDF(infile,outfile=None,overwrite=False):
"""
Uses command line "pdfcrop" on Linux (from tex installation) to clip a PDF to its bounding box.
Availability of this command (2016) makes the old Inkscape method obselete; in fact this is even more powerful [Well.. not 100% sure of that]
Hey, this looks promising! Otherwise, pdfcrop makes huge files!
http://tex.stackexchange.com/questions/42236/pdfcrop-generates-larger-file
"""
if outfile is None and overwrite and infile.endswith('.pdf'):
outfile = infile
if infile.endswith('.pdf'):
infile=infile[:-4]
if outfile is None and not overwrite:
outfile=infile+'-tightbb.pdf'
if not outfile.endswith('.pdf'):
outfile+='.pdf'
# No need to enclose this in a try catch, since os.system doesn't mind failing?
os.system(""" pdfcrop --margins 0 %s %s
echo "Wrote bounding-box cropped %s." """%(infile,outfile,outfile))
def tightBoundingBoxInkscape(infile,outfile=None,use_xvfb=False,overwrite=False):
"""Makes POSIX-specific OS calls. Need xvfb installed. If it fails anyway, could always resort to use_xvfb=False
Also, see https://github.com/skagedal/svgclip/blob/master/svgclip.py but I find rsvg buggy: it ignored the clipping boxes in my svg.
This would be entirely obselete due to pdfcrop working fine (command line), except that the Inkscape method produces tiny files while the pdfcrop makes them enormous.
"""
# Why is xvfb failing 2015 still?
#if use_xvfb is None:
# try:
# import os
# os.system('xvfb-run pwd')
# except:
usexvfb='xvfb-run '*use_xvfb # # +extension RANDR : does not work to fix
import os
from cpblUtilities import doSystem
if infile.endswith('.svg'):
assert outfile is None # Just overwrite SVG files.
doSystem("""
%(XVFB)s inkscape -f %(FN)s --verb=FitCanvasToDrawing --verb=FileSave --verb=FileQuit
"""%{'XVFB':usexvfb, 'FN':infile}, verbose=True,bg='ifserver')
return
if outfile is None and overwrite and infile.endswith('.pdf'):
# Random file name tempfile termporary file name (from stackexchange), without creating it yet:
#import uuid
outfile = infile#'/tmp/'+str(uuid.uuid4())
#overwritefile=infile
if infile.endswith('.pdf'):
infile=infile[:-4]
if outfile is None and not overwrite:
outfile=infile+'-tightbb.pdf'
if not outfile.endswith('.pdf'):
outfile+='.pdf'
# 2014: I should redesign this. First, do not change fonts on import. Second, can I skip the svg stage, with modern version?
doSystem("""
%(XVFB)s inkscape -f %(FN)s.pdf -l %(FN)s_tmp.svg
%(XVFB)s inkscape -f %(FN)s_tmp.svg --verb=FitCanvasToDrawing --verb=FileSave --verb=FileQuit
%(XVFB)s inkscape -f %(FN)s_tmp.svg -A %(OF)s
#rm %(FN)s_tmp.svg
"""%{'XVFB':usexvfb, 'FN':infile,'OF':outfile}, verbose=True,bg='ifserver')
if 0 and outfile is None and overwrite:
sout='cp %s %s'%(outfile,overwritefile)
print(' Overwriting original file!: '+sout)
os.system(sout)
def _no_tightBoundingBoxInkscape(infile,outfile=None,use_xvfb=False,overwrite=False):
""" Inkscape no nlonger necessary on Debian-based systems: just use pdfcrop.
The key to making either work is use savefigall() instead of savefig() (or to set the facecolor and transparent when using savefig)
Actually, this is false: Inkscape still produces small files, while pdfcrop's are huge.
"""
return tightBoundingBoxPDF(infile,outfile=outfile,overwrite=overwrite)
def saveAllFiguresToPDF(filename, figs=None, dpi=200):# : a trick to save all open figures. To a single PDF file.
from matplotlib.backends.backend_pdf import PdfPages
pp = PdfPages(filename)
if figs is None:
figs = [plt.figure(n) for n in plt.get_fignums()]
for fig in figs:
fig.savefig(pp, format='pdf')
pp.close()
##############################################################################
##############################################################################
#
def savefigall(fn, transparent=True, ifany=None, fig=None, skipIfExists=False, pauseForMissing=True, png = True, jpeg=False, jpeghi=False,svg=False, pdf=True, bw=False, FitCanvasToDrawing=False, eps=False, tikz=False, rv=None, facecolor='None', dpi=1000, overwrite=True, wh_inches=None):
##########################################################################
##########################################################################
"""
Like savefig, but implements important (transparent, facecolor) tweaks to ensure we can crop to bounding box, and it saves in multiple formats.
After many years, the bounding box cropping has started working suddenly in 2017.
Note that savefig() overwrites the figure's facecolor. So should always use this rather than built-in savefig(); or else always do something like
savefig('figname.png', facecolor=fig.get_facecolor(), transparent=True)
I think if you don't deal with facecolor, especially, then inkscape and pdfcrop will not work.
A new file with "-tightbb" suffix will be created when FitCanvasToDrawing is used, unless overwrite=True
April 2010: adding transparent option for png only. (right now it's automatic for pdf)
ifany must be an artist subclass taken by matplotlib.findobj(match=)
Do not forget to use pystata's
(self,figname=None,caption=None,texwidth=None,title=None,onlyPNG=False)
if you're using a cpbl latex object!
Sept 2010: skipIfExists is a way to skip saving if the file already exists (ie to save time).
Sept 2010: Returns written (or existing) file stems.
April 2011: oops: this has evolved to have dissimilar options from my latex class's saveAndIncludeFig() !
# saveAndIncludeFig(self,figname=None,caption=None,texwidth=None,title=None,onlyPNG=False,rcparams=None,transparent=False):
# Okay. April 2011: I've hopefully implemented things so can use latex.saveAnd... with the above options.
Nov 2010: Added "bw=False" option. If set to true, saves two sets, one with "-bw" suffix where colour has been turned to grayscale
Dec 2011: okay... when you start specifying fig size, the final boudning box no longer matches the plots. Crazy!
Online says there's a function to fix subplot params, but it doesn't seem to exist in my distro.
Here's an outlandish method: I'm making a very specific option, 'FitCanvasToDrawing', to implement it. It's outlandish because inkcape GUI gets called up and instantiated to do these verbs.
inkscape -f matplotlibOutput.pdf -l matplotlibOutput.svg
inkscape -f matplotlibOutput.svg --verb=FitCanvasToDrawing --verb=FileSave --verb=FileQuit
$ inkscape -f matplotlibOutput.svg -A prettyfigure.pdf
$ rm matplotlibOutput.svg
May 2012: Wow, if only the tikz converter matplotlib2tikz worked!! It fails in ways I cannot debug, from a colorbar or an envelope (patch), maybe. grr.
May 2012: adding rv=True and forcing transparent=True (below) !
Is transparent obselete, ie don't I always want it on? Maybe sometimes png was crashing. In 2013, remove it.
rv=True
rv=False
rv="both" and rv=None
2016 Sept: pdf output does not have transparency with "rv". Why?
June 2013: I still think use of facecolor and transparent in savefig is horribly buggy. I'm getting blue borders around all my figures now, except with rv. :(
2015: default resolution is now 1000 dpi (!), ie publication quality.
dpi: Resolution used for png format only.
widthheight: This is a tuple. Given in inches (sigh...). See plt.figure.get_figwidth() etc. NOT IMPLEMENTED YET!!!!!!!
Apr 2015: Fails when text includes \textwon. This is apparently on svg, while pdf and png work okay.
wh_inches: width and height of output in inches[sic!]
"""
#transparent=True # Huh? 2013 June: commenting this out.
bbox_inches="tight"
pad_inches=0
if FitCanvasToDrawing:
print("2017Jan: I don't think this Inkscape use is needed anymore. bbox_inches and pad_inches in savefig do the job.")
raw_input('acknowlege:')
(root,tail)=os.path.split(fn)
if bw:
savefigall(fn,transparent=transparent,ifany=ifany,fig=fig,skipIfExists=skipIfExists,pauseForMissing=pauseForMissing,png=png,jpeg=jpeg,jpeghi=jpeghi,svg=svg,pdf=pdf,FitCanvasToDrawing=FitCanvasToDrawing,eps=eps,tikz=tikz,rv=rv,facecolor=facecolor, wh_inches = wh_inches)
if 1: # ahhhh... issues jan 2012. kludge it off:
figureToGrayscale()
savefigall(fn+'-bw',transparent=transparent,ifany=ifany,fig=fig,skipIfExists=skipIfExists,pauseForMissing=pauseForMissing,png=png,jpeg=jpeg,jpeghi=jpeghi,svg=svg,pdf=pdf,FitCanvasToDrawing=FitCanvasToDrawing,eps=eps,tikz=tikz,rv=rv,facecolor=facecolor, wh_inches = wh_inches)
return(root+tail)
if fig is None:
fig=plt.gcf()
elif fig.__class__ in [mpl.figure.Figure]:
figure(fig.number)
else:
figure(fig)
if wh_inches is not None:
plt.gcf().set_figheight(wh_inches[1])
plt.gcf().set_figwidth(wh_inches[0])
if 0: # I need to use frozen() or deepcopy() to avoid rv and bw from messing up future modified versions of a plot! But this crashes:
fig=deepcopy(fig)
if rv in [True]:
figureToInverseVideo(fig, debug=False)
if rv in ['both',None]: # Do both.
assert not bw
# 28 June 2013: trying set transparent=True
savefigall(fn,transparent=True, #transparent,
ifany=ifany,fig=fig,skipIfExists=skipIfExists,pauseForMissing=pauseForMissing,png=png,jpeg=jpeg,jpeghi=jpeghi,svg=svg,pdf=pdf,rv=False,FitCanvasToDrawing=FitCanvasToDrawing,eps=eps,tikz=tikz,facecolor=facecolor)#facecolor)
if facecolor is None:
facecolor='k'
savefigall(fn+'-rv', transparent=transparent, ifany=ifany, fig=fig, skipIfExists=skipIfExists, pauseForMissing=pauseForMissing, png=png, jpeg=jpeg, jpeghi=jpeghi, svg=svg, pdf=pdf, rv=True, FitCanvasToDrawing=FitCanvasToDrawing, eps=eps, tikz=tikz, facecolor=facecolor)
return(root+tail)
if not root:
try:
from .cpblUtilities_config import defaults, paths
root=paths['graphics']#'/home/cpbl/rdc/workingData/graphics/'#defaults['workingPath']+'graphics/'#'graphicsPath'
except:
root='./'#/home/cpbl/rdc/graphicsOuttest/graphics/'#defaults['workingPath']+'graphics/'#'graphicsPath'
if root and not root.endswith('/'):
root+='/'
from cpblUtilities import str2pathname
tail=str2pathname(os.path.splitext(tail)[0]) # Get rid of any punctuation in the name.
if skipIfExists and os.path.exists(root+tail+'.png') and os.path.exists(root+tail+'.pdf'):
print ' Skipping production of '+root+tail+'(png/pdf) because it already exists [skipIfExists]'
return(root+tail)
if ifany:
if not plt.findobj(match=ifany):
print ' savefigall: Empty plot (no %s), so not saving %s.'%(str(ifany), root+tail)
plt.savefig(root+tail+'.png.FAILED', format='png', facecolor=facecolor, bbox_inches=bbox_inches, pad_inches=pad_inches)
if pauseForMissing:
plt.show()
from cpblUtilities import cwarning
cwarning(' savefigall: Empty plot (no %s), so not saving %s.'%(str(ifany),root+tail))
return(None)
def stupidOnlyUseGivenArguments(name,transparent=None,facecolor=None): #June 2013
if transparent and facecolor in ['None','none',None,False]:
fig.savefig(name,transparent=transparent,dpi=dpi, bbox_inches=bbox_inches, pad_inches=pad_inches)
elif transparent:
fig.savefig(name,transparent=transparent,facecolor=facecolor,dpi=dpi, bbox_inches=bbox_inches, pad_inches=pad_inches)
elif facecolor in ['None','none',None,False]:
fig.savefig(name,dpi=dpi, bbox_inches=bbox_inches, pad_inches=pad_inches)
else:
fig.savefig(name,facecolor=facecolor,dpi=dpi, bbox_inches=bbox_inches, pad_inches=pad_inches)
print 'Saving graphics: '+root+tail+' (+ext)'
if png:
stupidOnlyUseGivenArguments(root+tail+'.png',transparent=transparent,facecolor=facecolor)
if eps:
try: # Damn. Feb 2012 postscript is crashing.
plt.savefig(root+tail+'.eps',transparent=transparent,facecolor=facecolor, bbox_inches=bbox_inches, pad_inches=pad_inches)
except:
print('*****************\n\n\n\n\nFAILED TO PRODUCE AN EPS\n\n\n\n\n***********')
if pdf: # What?! I don't have to use stupidOnlyUseGivenArguments here? If I did it for PNG, everything's fine?
plt.savefig(root+tail+'.pdf',transparent=transparent,facecolor=facecolor, bbox_inches=bbox_inches, pad_inches=pad_inches)
if FitCanvasToDrawing:
#from cpblUtilities import doSystem
#"""
#xvfb is the /dev/null of X servers, if you like... ie normally command-line verbs of inkscape generate a gui, so this prefix hides them:
#"""
#print('You need xvfb installed. You need your .config/inkscape/extensions script. March 2014: XRANDR extension missing?! !"AAAAAAAAAAAAAARRRRCH')
if 0: print("""inkscape -f %(fn)s.pdf -l %(fn)sTMPTMPTMP.svg
inkscape -f %(fn)sTMPTMPTMP.svg --verb=FitCanvasToDrawing --verb=FileSave --verb=FileQuit
inkscape -f %(fn)sTMPTMPTMP.svg -A %(fn)s-autotrimmed.pdf
"""%{'fn':root+tail})
tightBoundingBoxInkscape(root+tail+'.pdf', overwrite=overwrite)#,use_xvfb=True) # ARGH March 2014: I'm getting an error from xvfb. So do it with GUI popping up!
if svg:
plt.savefig(root+tail+'.svg',transparent=transparent,facecolor=facecolor, bbox_inches=bbox_inches, pad_inches=pad_inches)
if tikz:
from matplotlib2tikz import save as tikz_save
tikz_save(root+tail+'_tikz.tex', figureheight='\\figureheight', figurewidth='\\figurewidth' )
if jpeg or jpeghi:
# jpeg Not supported by pylab!!
#plt.savefig(root+tail+'.jpeg')#transparent=True)
# 2013: I think jpeg is supported now. But "quality" keyword may not be.
plt.savefig(root+tail+'.jpeg',transparent=transparent,facecolor=facecolor, bbox_inches=bbox_inches, pad_inches=pad_inches)
#os.system('convert '+ root+tail+'.png '+root+tail+'.jpeg'+' &'*('apollo' in os.uname()[1]))
#if jpeghi:
#os.system('convert -quality 100 '+ root+tail+'.png '+root+tail+'.jpeg'+' &'*('apollo' in os.uname()[1]))
return(root+tail+FitCanvasToDrawing*'-autotrimmed')
if rv:
assert not bw
savefigall(fn,transparent=transparent,ifany=ifany,fig=fig,skipIfExists=skipIfExists,pauseForMissing=pauseForMissing,png=png,jpeg=jpeg,jpeghi=jpeghi,svg=svg,pdf=pdf,FitCanvasToDrawing=FitCanvasToDrawing,eps=eps,tikz=tikz,rv=rv)
def xTicksLogIncome_deprecated_USE_xTicksIncome(nticks=7,natlog=False,tenlog=False,kticks=None):
deprecated
def xTicksIncome(nticks=7,natlog=False,tenlog=False,kticks=None,dollarsign=True,lnUSA=False):
"""
Oct 2011: generalise above to deal with non-log?
For any plot in which the abscissa is log10(income) [set log10=True in this case] or log(income) , you can use this to make more readable tick points and labels.
You can specify the income ticks in k$ in kticks.
one of natlog or tenlog should be chose, until 2012 when I can set natlog=True as default, above. [not anymore, unless I make a new log=False argument]
June 2012: agh. lnUSA means that the income is in ln of USA level.
2014 Sept: this can be deprecated by a version of the X-or-y function which follows.
"""
#assert natlog or tenlog
assert not natlog or not tenlog
if not natlog and not tenlog and not lnUSA:
assert max(xlim())-min(xlim()) > 10 # Otherwise, it looks like we should have called natlog or tenlog
base,flog=10.0,np.log10
if natlog or lnUSA:
base,flog=np.e,np.log
if lnUSA: # I think the goal here is to list log 10 fractions?
assert not natlog
assert not tenlog
xl=pow(base,plt.array(plt.xlim()))
possibleTicks={.01:r'$\frac{1}{100}$',.0333333:r'$\frac{1}{30}$',.1:r'$\frac{1}{10}$',.33333:r'$\frac{1}{3}$',1:'1',2:'3'} # ,.5:r'$\frac{1}{2}$'
#{.02:r'$\frac{1}{50}$',.1:r'$\frac{1}{10}$',.5:r'$\frac{1}{2}$',1:'1',2:'2'}
ticks=sorted([ik for ik in possibleTicks if ik<=xl[1] and ik>=xl[0]])
plt.setp(plt.gca(),'xticks',[flog(cc) for cc in ticks])
plt.setp(plt.gca(),'xticklabels',[possibleTicks[cc] for cc in ticks])
elif natlog or tenlog:
xl=pow(base,plt.array(plt.xlim()))/1000
ones=[ik for ik in range(5,200,1) if ik<=xl[1] and ik>=xl[0]]
fives=[ik for ik in range(5,200,5) if ik<=xl[1] and ik>=xl[0]]
tens=[ik for ik in range(10,200,10) if ik<=xl[1] and ik>=xl[0]]
oo=[ones,fives,tens]
nn=[abs(len(ooo)-nticks) for ooo in oo]
choice=oo[[inn for inn in range(len(nn)) if nn[inn]==min(nn)][0]]
if kticks:
choice=kticks
plt.setp(plt.gca(),'xticks',[flog(cc*1000.0) for cc in choice])
plt.setp(plt.gca(),'xticklabels',[r'\$'*dollarsign+(str(cc)+'k' if cc>=1 else '%d'%(cc*1000)) for cc in choice])
else: #Linear values: just put a dollar sign and take off the thousands.?
xl=plt.array(plt.xlim())/1000.0
ones=[ik for ik in range(5,200,1) if ik<=xl[1] and ik>=xl[0]]
fives=[ik for ik in range(5,200,5) if ik<=xl[1] and ik>=xl[0]]
tens=[ik for ik in range(10,200,10) if ik<=xl[1] and ik>=xl[0]]
oo=[ones,fives,tens]
nn=[abs(len(ooo)-nticks) for ooo in oo]
choice=oo[[inn for inn in range(len(nn)) if nn[inn]==min(nn)][0]]
if kticks:
choice=kticks
plt.setp(plt.gca(),'xticks',[cc*1000.0 for cc in choice])
plt.setp(plt.gca(),'xticklabels',[r'\$'*dollarsign+str(cc)+'k' for cc in choice])
return
def xyTicksIncome(nticks=7,natlog=False,tenlog=False,kticks=None,dollarsign=True,lnUSA=False,XorY='x'):
"""
2014 Sept: copied from xTicksIncome.
Not sure how to merge these yet.
It seems this is rather targeted for US$ amounts. Not, for instance, Korean won!
"""
if XorY in ['x']:
XLIM,xticks,xticklabels=plt.xlim,'xticks','xticklabels'
else:
XLIM,xticks,xticklabels=plt.ylim,'yticks','yticklabels'
#assert natlog or tenlog
assert not natlog or not tenlog
if not natlog and not tenlog and not lnUSA:
assert max(XLIM())-min(XLIM()) > 10 # Otherwise, it looks like we should have called natlog or tenlog
base,flog=10.0,np.log10
if natlog or lnUSA:
base,flog=np.e,np.log
if lnUSA: # I think the goal here is to list log 10 fractions?
assert not natlog
assert not tenlog
xl=pow(base,plt.array(XLIM()))
possibleTicks={.01:r'$\frac{1}{100}$',.0333333:r'$\frac{1}{30}$',.1:r'$\frac{1}{10}$',.33333:r'$\frac{1}{3}$',1:'1',2:'3'} # ,.5:r'$\frac{1}{2}$'
#{.02:r'$\frac{1}{50}$',.1:r'$\frac{1}{10}$',.5:r'$\frac{1}{2}$',1:'1',2:'2'}
ticks=sorted([ik for ik in possibleTicks if ik<=xl[1] and ik>=xl[0]])
plt.setp(plt.gca(),xticks,[flog(cc) for cc in ticks])
plt.setp(plt.gca(),xticklabels,[possibleTicks[cc] for cc in ticks])
elif natlog or tenlog:
xl=pow(base,plt.array(XLIM()))/1000
ones=[ik for ik in range(5,200,1) if ik<=xl[1] and ik>=xl[0]]
fives=[ik for ik in range(5,200,5) if ik<=xl[1] and ik>=xl[0]]
tens=[ik for ik in range(10,200,10) if ik<=xl[1] and ik>=xl[0]]
oo=[ones,fives,tens]
nn=[abs(len(ooo)-nticks) for ooo in oo]
choice=oo[[inn for inn in range(len(nn)) if nn[inn]==min(nn)][0]]
if kticks:
choice=kticks
plt.setp(plt.gca(),xticks,[flog(cc*1000.0) for cc in choice])
plt.setp(plt.gca(),xticklabels,[r'\$'*dollarsign+(str(cc)+'k' if cc>=1 else '%d'%(cc*1000)) for cc in choice])
else: #Linear values: just put a dollar sign and take off the thousands.?
xl=plt.array(XLIM())/1000.0
ones=[ik for ik in range(5,200,1) if ik<=xl[1] and ik>=xl[0]]
fives=[ik for ik in range(5,200,5) if ik<=xl[1] and ik>=xl[0]]
tens=[ik for ik in range(10,200,10) if ik<=xl[1] and ik>=xl[0]]
oo=[ones,fives,tens]
nn=[abs(len(ooo)-nticks) for ooo in oo]
choice=oo[[inn for inn in range(len(nn)) if nn[inn]==min(nn)][0]]
if kticks:
choice=kticks
plt.setp(plt.gca(),xticks,[cc*1000.0 for cc in choice])
plt.setp(plt.gca(),xticklabels,[r'\$'*dollarsign+str(cc)+'k' for cc in choice])
return
def yTicksIncome(nticks=7,natlog=False,tenlog=False,kticks=None,dollarsign=True,lnUSA=False):
return(
xyTicksIncome(nticks=nticks,natlog=natlog,tenlog=tenlog,kticks=kticks,dollarsign=dollarsign,lnUSA=lnUSA,XorY='y')
)
def xyticksExponentiate(base10=False, x=True, y=False):
"""
The x-values are log or log10, but I want the labels to show the unlogged values
N.B. Only shows integer values.
"""
def pow2base10(vv,pos): #'The two args are the value and tick position'
if int(vv)==vv:
if vv<2 and vv>=0:
return(str(int(pow(10,vv))))
return(r'10$^{%s}$'%int(vv) )
return('') # Hide all non-integer values!?
if base10 and x:
gca().xaxis.set_major_formatter( mpl.ticker.FuncFormatter(pow2base10))
if base10 and y:
gca().yaxis.set_major_formatter( mpl.ticker.FuncFormatter(pow2base10))
##############################################################################
##############################################################################
#
def stdev(vals):
##########################################################################
##########################################################################
""" the available std does not give 0 if there's only one value
"""
from pylab import std
if len(vals)>1:
return(std(vals))
elif len(vals)>0:
return(0.)
else:
return (float('nan'))
##############################################################################
##############################################################################
#
from numpy import mean # This one deals with no values!, so overwrite python's default mean..
#def mean(vals):
# ##########################################################################
# ##########################################################################
# """ the available mean cannot deal with no values
# """
# import pylab
# if len(vals)>0:
# return(np.mean(vals))
# else:
# return(float('nan'))
##############################################################################
##############################################################################
#
# Following renamed from seMean().
def mean_of_means(vals, ses): # A Weighted mean: weighted by standard errors
# Takes a simple list of estimates and a simple list of their standard errors.
# Returns an estimate of the scalar weighted mean and its standard error.
#
# See wtsem() for se of the mean of a list of values.
#
# - COVARIANCE IS IGNORED (ASSUMED ZERO)
#
# - NaNs are DROPPED! before taking mean
##########################################################################
##########################################################################
#from pylab import mean #import pylab
from pylab import sqrt,isnan
if list(vals) and any(vals):
vals,ses=[v for v in vals if not isnan(v)],[v for v in ses if not isnan(v)]
meanSE=sqrt(1.0/sum([1.0/ff/ff for ff in ses]))
meanVals=sum([vals[ic]/ses[ic]/ses[ic] for ic in range(len(vals))])*meanSE*meanSE
else:
meanSE=''#[float('nan')]
meanVals=''#[float('nan')]
return(meanVals,meanSE)
##############################################################################
##############################################################################
#
def seSum(x=None, sx=None): # This takes a simple sum across x, i.e. x has length >1.
##########################################################################
##########################################################################
#from pylab import mean #import pylab
from pylab import sqrt,array,isnan,any
f=sum(x)
sf=sqrt(sum(array(sx)*array(sx)))
assert not any(isnan(sf))
return(f,sf)
##############################################################################
##############################################################################
#
def seProduct(x=None,y=None,sx=None,sy=None,covs=None):
##########################################################################
##########################################################################
"""
For f=x*y, with s.e.'s of sx and sy, returns f and approx sf...
# COVARIANCE SO FAR IGNORED!
They need to be floats at the moment.
The arguments are mandatory; I'm making them parameters for easier reading of the call.