-
Notifications
You must be signed in to change notification settings - Fork 0
/
table.py
259 lines (233 loc) · 11.9 KB
/
table.py
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
import decimal as _dc
from collections import Counter as _Counter
from string import printable as _printable
from sys import stdin as _stdin
from numpy import array as _array
_ArgumentError = TypeError(
'tex_table function eat args which are (string, AbstractVar) or (string, AbstractVar, bool) or tuple '
'of such objects.') # Also it eats GroupVar and tuple of names for each??? <-Надо ли это?
def rus_tex_formula(formula: str) -> str:
# Будьте аккуратны!
# Данная чудо-функция неверно обрабатывает формулы, в которых русский используется для написания в
# различных местах без английских символов между русскими блоками
# Например:
# Так_н е работает -> \text{Так}_\text{н е работает}
# А_{так} работает -> \text{А}_{\text{так}} \text{работает}
"""
К сожалению TeX не поддерживает русский язык в формулах. Для этого есть \text. Чтобы обращаться с русским так же,
как и с ангийским в формулах, есть этот преобразватель. Правда работает он через пень колоду (см. комментарий выше)
"""
ret = ''
rus_now = False
for chr in formula:
if chr in ' ,.!?:;':
ret += chr
elif chr in _printable:
if rus_now is True:
ret += '}' + chr
rus_now = False
else:
ret += chr
else:
if rus_now is True:
ret += chr
else:
ret += '\\text{' + chr
rus_now = True
if rus_now is True: ret += '}'
return ret
def tex_table(*args,
caption: str = '', numerate: bool = True,
colors=('C0C0C0', 'EFEFEF', 'C0C0C0',), color_frequency: int = 2,
accuracy: float = 0.2, lab_fmt: bool = True):
"""
Проще объяснить на примере
Есть серии измерений теплоёмкости и температуры.
T=Var(range(273,280), [1]*7)
C=Var([9]*7, [0.1]*7)
T измеряется в К, а С в Дж/К
:param args: ('T, К', T), ('С, Дж/К', C)
или (('T, К', T), ('С, Дж/К', C),)
Сгенерируется таблица со значениями и погрешностями. Будут столбцы: 'T, К', '\Delta T, К', 'С, Дж/К', '\Delta С, Дж/К'
Но если вам, например, не захотелось отображать погрешность T, то args должно иметь вид:
('T, К', T, False), ('С, Дж/К', C)
:param caption: guess yourself
:param numerate: Вводит нумерацию строчек
:param colors: Таблица имеет разные цвета: цвет заголовков, цвет чередования, цвет столбца нумепрации.
В формате цветов HTML можно менять их.
:param color_frequency: Частота чередования
:param accuracy: Точность, с которой отображается ошибка
:param lab_fmt: Выносит степени 10 в заголовок
"""
# This func should check: Do this arg should present error in table or not?
def ch_err(arg):
return (len(arg) == 2) or arg[2] is True
if hasattr(args[0], '__getitem__') and not hasattr(args[0][0], 'split'):
args = args[0]
try:
height = args[0][1].__len__()
except Exception:
raise _ArgumentError
table = list()
if numerate:
table.append([''] + list(map(str, range(1, height + 1))))
NCol = 0 # Номер текущей колонки
else:
NCol = -1 # Номер текущей колонки
for arg in args:
if len(arg[1]) is not height:
raise TypeError('Wrong length of arguments')
NCol += 1
if lab_fmt is True:
if ch_err(arg):
# table.append(['$' + rus_tex_formula(arg[0]) + '$'])
val = tuple(map(lambda x: _dc.Decimal(str(x)), arg[1].val()))
err = tuple(map(lambda x: _dc.Decimal(str(x)), arg[1].err()))
most_common_exp = _Counter(_get_eng_exp(v) for v in val).most_common(1)[0][0]
if most_common_exp != 0:
# Наличие ',' значит поставил ли пользователь размерность или это безразмерное число
if ',' in arg[0]:
table.append(['$' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(most_common_exp) + '} $'])
else:
table.append(['$' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(-most_common_exp) + '} $'])
else:
table.append(['$' + rus_tex_formula(arg[0]) + '$'])
Arr_of_val = list()
Arr_of_err = list()
for i in range(height):
Rval, Rerr = _lab_decimal_style(val[i], err[i], accuracy=accuracy)
Arr_of_val += [_dec_normal(Rval.scaleb(-most_common_exp))]
Arr_of_err += [_dec_normal(Rerr.scaleb(-most_common_exp))]
table[NCol] += Arr_of_val
if most_common_exp != 0:
# Наличие ',' значит поставил ли пользователь размерность или это безразмерное число
if ',' in arg[0]:
table.append(
['$' + '\\Delta ' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(most_common_exp) + '} $'])
else:
table.append(['$' + '\\Delta ' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(
-most_common_exp) + '} $'])
else:
table.append(['$' + '\\Delta ' + rus_tex_formula(arg[0]) + '$'])
NCol += 1
table[NCol] += Arr_of_err
else:
# table.append(['$' + rus_tex_formula(arg[0]) + '$'])
val = tuple(map(lambda x: _dc.Decimal(str(x)), arg[1].val()))
most_common_exp = _Counter(_get_eng_exp(v) for v in val).most_common(1)[0][0]
if most_common_exp != 0:
# Наличие ',' значит поставил ли пользователь размерность или это безразмерное число
if ',' in arg[0]:
table.append(['$' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(most_common_exp) + '} $'])
else:
table.append(['$' + rus_tex_formula(arg[0]) + '\\cdot 10^{' + str(-most_common_exp) + '} $'])
else:
table.append(['$' + rus_tex_formula(arg[0]) + '$'])
Arr_of_val = list()
for i in range(height):
Rval, Rerr = _lab_decimal_style(val[i], 0, accuracy=accuracy)
Arr_of_val += [_dec_normal(Rval.scaleb(-most_common_exp))]
table[NCol] += Arr_of_val
else:
table.append(['$' + rus_tex_formula(arg[0]) + '$'])
if ch_err(arg):
table[NCol] += tuple(map(str, arg[1].val()))
NCol += 1
table[NCol] += tuple(map(str, arg[1].err()))
else:
table[NCol] += list(map(str, arg[1].val()))
NCol += 1 # Отныне это число колонок без учёта нумерации (самая левая колонка)
ret = '\\begin{table}[ht]' + '\n' + '\\center' + '\n'
if caption is None or False:
pass
else:
ret += '\\caption{' + caption + '}' + '\n'
ret += '\\begin{tabular}{' + len(table) * '|c' + '|}' + '\n' + '\\hline' + '\n' + \
'\\rowcolor[HTML]{' + colors[0] + '}' + '\n'
for str_num in range(height + 1):
if str_num % color_frequency == 0 and str_num != 0:
ret += '\\rowcolor[HTML]{' + colors[1] + '} \n'
if numerate and str_num != 0:
ret += '\\cellcolor[HTML]{' + colors[2] + '} '
for col_num in range(NCol):
ret += table[col_num][str_num] + ' & '
ret = ret[:-2] + '\\\\ \\hline' + '\n'
ret += '\\end{tabular}' + '\n' + '\\end{table}' + '\n'
return ret
def _get_eng_exp(x):
return divmod(int(x.logb()), 3)[0] * 3
def _lab_decimal_style(val, err, accuracy=0.05):
if err != 0:
Rerr = err.quantize(_dc.Decimal('1').scaleb(_dc.Decimal(str(_dc.Decimal(accuracy) * err)).logb()),
rounding=_dc.ROUND_HALF_UP).normalize()
Rval = val.quantize(Rerr,
rounding=_dc.ROUND_HALF_UP)
return Rval, Rerr
else:
if val != 0:
Rval = val.quantize(_dc.Decimal('1').scaleb(_dc.Decimal(str(_dc.Decimal(accuracy) * val)).logb()),
rounding=_dc.ROUND_HALF_UP).normalize()
return Rval, _dc.Decimal(0)
else:
return _dc.Decimal(0), _dc.Decimal(0)
def XL_to_table(text: str = None):
"""
Вводишь копию ячеек из XL - получаешь питоновские списки списков.
В случае отсутствия параметров, принимает то же самое, но не в качестве аргумента, а из stdin
"""
if text is None:
text = _stdin
arr = text.readline().strip()
table = list(list() for i in range(len(arr)))
while arr != '':
arr = list(map(float, (arr.split('\t'))))
for i in range(len(arr)):
table[i].append(arr[i])
arr = text.readline().rstrip()
return table
table = tuple(map(lambda x: x.split('\t'), text.split('\n')))[:-1]
t = tuple(map(lambda x: tuple(filter(lambda y: y!='', x)), table))
table=_array(tuple(map(lambda x: tuple(map(float, x)),t)))
return transpose(table)
def table_to_XL(table, t=True):
"""
Хочешь перевести питоновские списки списков в Exel? Тебе сюда.
Выводит строку, которую можно вставить в Exel. (И получить ячейки)
Если надо выодить данные в строку, в не в столбец - t=False.
"""
ret = ''
if t:
table = tuple(zip(*table))
if len(table) == 1 and not hasattr(table[0], '__iter__'):
for cell in table:
ret += str(cell) + '\n'
return ret[:-1]
for col in table:
for cell in col:
try:
ret += str(cell) + '\t'
except:
ret += '\t'
ret += '\n'
return ret[:-1]
def _dec_normal(d):
return '{0:f}'.format(d)
def dec_alone_style(x, accuracy=0.3):
Rval, Rerr = _lab_decimal_style(_dc.Decimal(x.val()), _dc.Decimal(x.err()), accuracy=accuracy)
return _dec_normal(Rval), _dec_normal(Rerr)
def transpose(x, nparray = True):
l = max(tuple(map(len, x)))
res=[]
for c in range(l):
res.append([])
for s in range(len(x)):
try:
res[c].append(x[s][c])
except:
pass
if nparray:
res = list(map(_array, res))
return res
def lab_decimal_style(x, accuracy=0.2):
d = dec_alone_style(x, accuracy=accuracy)
return d[0]+" \\pm "+d[1]