Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Classes which defines column for class @see cl IterRow 

5""" 

6from inspect import isfunction 

7from .iter_exceptions import IterException, NotAllowedOperation 

8from .others_types import long, NA, EmptyGroup, GroupByContainer 

9 

10from .column_operator import OperatorId, OperatorMul, ColumnOperator, OperatorAdd 

11from .column_operator import OperatorDiv, OperatorPow, OperatorSub, OperatorDivN, OperatorMod 

12from .column_operator import OperatorEq, OperatorNe, OperatorGe, OperatorLe, OperatorGt, OperatorLt 

13from .column_operator import OperatorNot, OperatorOr, OperatorAnd 

14from .column_operator import OperatorFunc 

15 

16from .column_group_operator import OperatorGroupLen, OperatorGroupAvg 

17 

18 

19def private_function_type(): 

20 "no documentation" 

21 pass 

22 

23 

24class ColumnType: 

25 

26 """ 

27 Defines a column of a table. 

28 """ 

29 

30 _default_name = "__unk__" 

31 _str_type = {int: 'int', long: 'long', NA: 'NA', 

32 float: 'float', str: 'str', 

33 type(private_function_type): 'func', 

34 } 

35 

36 def IsColumnType(self): 

37 """ 

38 checks it is a column type which used by an operator 

39 """ 

40 return True 

41 

42 @property 

43 def ShortName(self): 

44 """ 

45 a short name (tells the column type) 

46 """ 

47 return "any" 

48 

49 @property 

50 def Name(self): 

51 """ 

52 property 

53 """ 

54 return self._name 

55 

56 @property 

57 def Type(self): 

58 """ 

59 property 

60 """ 

61 return self._type 

62 

63 @property 

64 def Parent(self): 

65 """ 

66 property 

67 """ 

68 return self._parent 

69 

70 @property 

71 def Func(self): 

72 """ 

73 property 

74 """ 

75 return self._func 

76 

77 def __init__( 

78 self, name, typ, func=None, parent=tuple(), op=None, owner=None): 

79 """ 

80 initiates the column 

81 

82 @param name name of the column 

83 @param typ type of the data it will contain (can be None) 

84 @param func a function, if None, if will be the identity 

85 @param parent precise a list of parents if this column was part of a formula 

86 @param op operator to apply between the column 

87 @param owner table which contains the column (only for further validation) 

88 

89 function is a function: ``f: x --> y`` 

90 """ 

91 self._name = name 

92 self._type = typ 

93 self._value = None 

94 self._parent = parent 

95 self._op = op 

96 self._owner = owner 

97 

98 if not isinstance(op, ColumnOperator): 

99 raise IterException( 

100 "op should be a ColumnOperator not: {0}".format( 

101 type(op))) 

102 

103 if not isinstance(parent, tuple): 

104 raise TypeError("we expect a tuple for parameter parent") 

105 for p in parent: 

106 p.IsColumnType() 

107 

108 if typ not in [int, float, long, str, None, NA, 

109 type(private_function_type)]: 

110 raise IterException( 

111 "type should in [int,float,str,long,function]: " + 

112 str(typ)) 

113 

114 if isfunction(func): 

115 self._func = func 

116 elif func is None: 

117 self._func = None 

118 else: 

119 raise IterException( 

120 "type of func should in [int,float,str,long,function]: {0}".format( 

121 str(func))) 

122 

123 if "_func" not in self.__dict__: 

124 raise IterException("this column is missing a function") 

125 

126 def __str__(self): 

127 """ 

128 usual 

129 """ 

130 ps = "|".join([_.ShortName for _ in self._parent]) 

131 if self._value is not None: 

132 return "CT({0},<{1}>,op:{2},#P={3})={4}".format( 

133 self._name, ColumnType._str_type[self._type], str(self._op), ps, self()) 

134 else: 

135 return "CT({0},<{1}>,op:{2},#P={3}) [no loop started]".format( 

136 self._name, ColumnType._str_type[self._type], str(self._op), ps) 

137 

138 def __call__(self): 

139 """ 

140 returns func(value) 

141 """ 

142 if self._func is None: 

143 if len(self._parent) == 0: 

144 if self._value is None: 

145 raise ValueError( 

146 "method set must be called before for column {0}".format( 

147 str(self))) 

148 else: 

149 res = self._value 

150 elif self._op is None: 

151 raise ValueError( 

152 "there are parents but no operator for column {0}\nParents:\n{1}".format( 

153 str(self), 

154 self.print_parent())) 

155 else: 

156 try: 

157 res = self._op(self._parent) 

158 except TypeError as e: 

159 raise IterException( 

160 "unable(1) to apply an operator for column op=<{0}>, col={1}, TYPE={2} TYPE_OP={3} TYPE_PARENT={4}".format( 

161 str( 

162 self._op), str(self), type(self), type( 

163 self._op), type( 

164 self._parent))) from e 

165 except AttributeError as ee: 

166 raise IterException( 

167 "unable(2) to apply an operator for column op=<{0}>, col={1}, TYPE={2} TYPE_OP={3} TYPE_PARENT={4}".format( 

168 str( 

169 self._op), str(self), type(self), type( 

170 self._op), type( 

171 self._parent))) from ee 

172 

173 if isinstance(res, ColumnType): 

174 raise IterException( 

175 "this evaluation(*) cannot return a ColumnType for this column: {0}".format(str(self))) 

176 else: 

177 # we use a shortcut 

178 try: 

179 res = self._func(self._value) 

180 except TypeError as e: 

181 raise IterException( 

182 "unable to compute the value of {0}\n{1}".format( 

183 str(self), 

184 self.print_parent())) from e 

185 

186 if isinstance(res, ColumnType): 

187 raise IterException( 

188 "this evaluation cannot return a ColumnType for this column: {0}".format( 

189 str(self))) 

190 

191 self.set(res) 

192 return res 

193 

194 def set(self, value): 

195 """ 

196 Sets a value for this column. 

197 

198 @param value anything in ``[int, float, long, str, function]`` 

199 """ 

200 if isinstance(value, (int, str, float, long, NA)): 

201 self._value = value 

202 elif isinstance(value, EmptyGroup): 

203 # for an empty group 

204 self._value = value 

205 elif isinstance(value, list): 

206 # for a group 

207 self._value = value 

208 else: 

209 raise IterException( 

210 "type of value should be in [int,float,str,long] not {0} for the column {1}".format( 

211 type(value), 

212 str(self))) 

213 

214 def set_none(self): 

215 """ 

216 After a loop on a database, we should put None back as a value. 

217 """ 

218 for p in self._parent: 

219 p.set_none() 

220 self._value = None 

221 

222 def set_name(self, new_name): 

223 """ 

224 Changes the name of the column. 

225 

226 @param newname new name 

227 """ 

228 self._name = new_name 

229 

230 def set_owner(self, new_owner): 

231 """ 

232 Changes the owner of the column. 

233 

234 @param newname new name 

235 """ 

236 self._owner = new_owner 

237 

238 def print_parent(self): 

239 """ 

240 Returns a string showing the dependencies of this columns. 

241 

242 Example: 

243 @code 

244 this_columns 

245 parent1 

246 parent11 

247 parent12 

248 parent2 

249 @endcode 

250 """ 

251 if self._parent is None: 

252 return self.__str__() 

253 else: 

254 rows = [self.__str__()] 

255 for p in self._parent: 

256 rs = [" " + _ for _ in p.print_parent().split("\n")] 

257 rows.extend(rs) 

258 return "\n".join(rows) 

259 

260 ###################################### 

261 # functions which create other columns 

262 ###################################### 

263 

264 def copy(self, new_owner): 

265 """ 

266 Returns a copy of this class. 

267 

268 @param new_owner new owner 

269 @return ColumnType 

270 """ 

271 return ColumnType(self._name, self._type, func=None, parent=( 

272 self,), op=OperatorId(), owner=new_owner) 

273 

274 ####################################### 

275 # operations 

276 ####################################### 

277 

278 def __mul__(self, column): 

279 """ 

280 These operators should be able to translate an expression 

281 into function operating on the values. 

282 

283 @param column a function or an int or a float or a long or a str or a ColumnType 

284 @return a ColumnType 

285 """ 

286 if isinstance(column, ColumnType): 

287 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

288 self, column), op=OperatorMul()) 

289 else: 

290 return self.__mul__(ColumnConstantType(column)) 

291 

292 def __add__(self, column): 

293 """ 

294 These operators should be able to translate an expression 

295 into function operating on the values. 

296 

297 @param column a function or an int or a float or a long or a str or a ColumnType 

298 @return a ColumnType 

299 """ 

300 if isinstance(column, ColumnType): 

301 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

302 self, column), op=OperatorAdd()) 

303 else: 

304 return self.__add__(ColumnConstantType(column)) 

305 

306 def __sub__(self, column): 

307 """ 

308 These operators should be able to translate an expression 

309 into function operating on the values. 

310 

311 @param column a function or an int or a float or a long or a str or a ColumnType 

312 @return a ColumnType 

313 """ 

314 if isinstance(column, ColumnType): 

315 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

316 self, column), op=OperatorSub()) 

317 else: 

318 return self.__sub__(ColumnConstantType(column)) 

319 

320 def __truediv__(self, column): 

321 """ 

322 These operators should be able to translate an expression 

323 into function operating on the values. 

324 

325 @param column a function or an int or a float or a long or a str or a ColumnType 

326 @return a ColumnType 

327 """ 

328 if isinstance(column, ColumnType): 

329 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

330 self, column), op=OperatorDiv()) 

331 else: 

332 return self.__truediv__(ColumnConstantType(column)) 

333 

334 def __floordiv__(self, column): 

335 """ 

336 These operators should be able to translate an expression 

337 into function operating on the values. 

338 

339 @param column a function or an int or a float or a long or a str or a ColumnType 

340 @return a ColumnType 

341 """ 

342 if isinstance(column, ColumnType): 

343 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

344 self, column), op=OperatorDivN()) 

345 else: 

346 return self.__floordiv__(ColumnConstantType(column)) 

347 

348 def __mod__(self, column): 

349 """ 

350 these operators should be able to translate an expression 

351 into function operating on the values 

352 

353 @param column a function or an int or a float or a long or a str or a ColumnType 

354 @return a ColumnType 

355 """ 

356 if isinstance(column, ColumnType): 

357 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

358 self, column), op=OperatorMod()) 

359 else: 

360 return self.__mod__(ColumnConstantType(column)) 

361 

362 def __pow__(self, column): 

363 """ 

364 these operators should be able to translate an expression 

365 into function operating on the values 

366 

367 @param column a function or an int or a float or a long or a str or a ColumnType 

368 @return a ColumnType 

369 """ 

370 if isinstance(column, ColumnType): 

371 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

372 self, column), op=OperatorPow()) 

373 else: 

374 return self.__pow__(ColumnConstantType(column)) 

375 

376 ####################################### 

377 # test 

378 ####################################### 

379 

380 def __eq__(self, column): 

381 """ 

382 these operators should be able to translate an expression 

383 into function operating on the values 

384 

385 @param column a function or an int or a float or a long or a str or a ColumnType 

386 @return a ColumnType 

387 """ 

388 if isinstance(column, ColumnType): 

389 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

390 self, column), op=OperatorEq()) 

391 else: 

392 return self.__eq__(ColumnConstantType(column)) 

393 

394 def __lt__(self, column): 

395 """ 

396 these operators should be able to translate an expression 

397 into function operating on the values 

398 

399 @param column a function or an int or a float or a long or a str or a ColumnType 

400 @return a ColumnType 

401 """ 

402 if isinstance(column, ColumnType): 

403 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

404 self, column), op=OperatorLt()) 

405 else: 

406 return self.__lt__(ColumnConstantType(column)) 

407 

408 def __le__(self, column): 

409 """ 

410 these operators should be able to translate an expression 

411 into function operating on the values 

412 

413 @param column a function or an int or a float or a long or a str or a ColumnType 

414 @return a ColumnType 

415 """ 

416 if isinstance(column, ColumnType): 

417 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

418 self, column), op=OperatorLe()) 

419 else: 

420 return self.__le__(ColumnConstantType(column)) 

421 

422 def __gt__(self, column): 

423 """ 

424 these operators should be able to translate an expression 

425 into function operating on the values 

426 

427 @param column a function or an int or a float or a long or a str or a ColumnType 

428 @return a ColumnType 

429 """ 

430 if isinstance(column, ColumnType): 

431 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

432 self, column), op=OperatorGt()) 

433 else: 

434 return self.__gt__(ColumnConstantType(column)) 

435 

436 def __ge__(self, column): 

437 """ 

438 these operators should be able to translate an expression 

439 into function operating on the values 

440 

441 @param column a function or an int or a float or a long or a str or a ColumnType 

442 @return a ColumnType 

443 """ 

444 if isinstance(column, ColumnType): 

445 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

446 self, column), op=OperatorGe()) 

447 else: 

448 return self.__ge__(ColumnConstantType(column)) 

449 

450 def __ne__(self, column): 

451 """ 

452 these operators should be able to translate an expression 

453 into function operating on the values 

454 

455 @param column a function or an int or a float or a long or a str or a ColumnType 

456 @return a ColumnType 

457 """ 

458 if isinstance(column, ColumnType): 

459 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

460 self, column), op=OperatorNe()) 

461 else: 

462 return self.__ne__(ColumnConstantType(column)) 

463 

464 ####################################### 

465 # logical 

466 ####################################### 

467 

468 def Not(self): 

469 """ 

470 ``not`` cannot be overriden 

471 """ 

472 return self.__not__() 

473 

474 def __not__(self): 

475 """ 

476 these operators should be able to translate an expression 

477 into function operating on the values 

478 

479 @return a ColumnType 

480 """ 

481 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

482 self,), op=OperatorNot()) 

483 

484 def Or(self, column): 

485 """ 

486 ``or`` cannot be overriden 

487 """ 

488 return self.__or__(column) 

489 

490 def __or__(self, column): 

491 """ 

492 these operators should be able to translate an expression 

493 into function operating on the values 

494 

495 @param column a function or an int or a float or a long or a str or a ColumnType 

496 @return a ColumnType 

497 """ 

498 if isinstance(column, ColumnType): 

499 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

500 self, column), op=OperatorOr()) 

501 else: 

502 return self.__or__(ColumnConstantType(column)) 

503 

504 def And(self, column): 

505 """ 

506 ``and`` cannot be overriden 

507 """ 

508 return self.__and__(column) 

509 

510 def __and__(self, column): 

511 """ 

512 these operators should be able to translate an expression 

513 into function operating on the values 

514 

515 @param column a function or an int or a float or a long or a str or a ColumnType 

516 @return a ColumnType 

517 """ 

518 if isinstance(column, ColumnType): 

519 return ColumnType(ColumnType._default_name, self._type, func=None, parent=( 

520 self, column), op=OperatorAnd()) 

521 else: 

522 return self.__and__(ColumnConstantType(column)) 

523 

524 ####################################### 

525 # group function 

526 ####################################### 

527 

528 def len(self): 

529 """ 

530 returns a group columns to count the number of observations 

531 """ 

532 return ColumnGroupType( 

533 ColumnType._default_name, int, parent=(self,), op=OperatorGroupLen()) 

534 

535 def count(self): 

536 """ 

537 returns a group columns to count the number of observations 

538 """ 

539 return self.len() 

540 

541 def avg(self): 

542 """ 

543 returns a group columns to return an average 

544 """ 

545 return ColumnGroupType( 

546 ColumnType._default_name, float, parent=(self,), op=OperatorGroupAvg()) 

547 

548 

549class ColumnConstantType(ColumnType): 

550 

551 """ 

552 defines a constant as a column 

553 """ 

554 

555 def __init__(self, const): 

556 self._value = const 

557 self._func = lambda x, c=self._value: c 

558 self._parent = None 

559 self._op = None 

560 self._type = type(const) 

561 self._const = const 

562 self._owner = None 

563 

564 if isinstance(const, (int, float, long, str, NA)): 

565 pass 

566 else: 

567 raise ValueError( 

568 "this value is not a constant: {0}".format( 

569 str(const))) 

570 

571 @property 

572 def ShortName(self): 

573 """ 

574 a short name (tells the column type) 

575 """ 

576 return "cst" 

577 

578 def set_none(self): 

579 """ 

580 do nothing (it is a constant) 

581 """ 

582 pass 

583 

584 def set(self, value): 

585 """ 

586 do nothing (it is a constant) 

587 

588 @param value anything in [int,float,long,str, function ] 

589 """ 

590 pass 

591 

592 def __call__(self): 

593 """ 

594 return the constant 

595 """ 

596 return self._const 

597 

598 def __str__(self): 

599 """ 

600 usual 

601 """ 

602 return "cst({0})".format(self()) 

603 

604 

605class ColumnTableType(ColumnType): 

606 

607 """ 

608 defines a table column (not coming from an expression) 

609 """ 

610 

611 def __init__(self, name, typ, owner): 

612 """ 

613 constructor 

614 

615 @param name name of the column 

616 @param typ type of the column 

617 @param owner owner of this column 

618 """ 

619 self._name = name 

620 self._func = None 

621 self._parent = None 

622 self._op = None 

623 self._type = typ 

624 self._owner = owner 

625 

626 @property 

627 def ShortName(self): 

628 """ 

629 a short name (tells the column type) 

630 """ 

631 return "col" 

632 

633 def set_none(self): 

634 """ 

635 after a loop on a database, we should put None back as a value 

636 """ 

637 self._value = None 

638 

639 def __call__(self): 

640 """ 

641 returns the content 

642 """ 

643 if self._value is None: 

644 raise IterException( 

645 "this column should contain a value: {0}".format( 

646 str(self))) 

647 return self._value 

648 

649 def __str__(self): 

650 """ 

651 usual 

652 """ 

653 return "col({0},{1})".format( 

654 self._name, ColumnType._str_type[self._type]) 

655 

656 

657class ColumnGroupType(ColumnType): 

658 

659 """ 

660 defines a column which processes a group of rows (after a groupby) 

661 """ 

662 

663 def __init__(self, name, typ, parent, op): 

664 """ 

665 constructor 

666 

667 @param name name of the column 

668 @param typ type of the column 

669 @param owner owner of this column 

670 @param op operator 

671 """ 

672 self._name = name 

673 self._value = None 

674 self._parent = parent 

675 self._opgr = op 

676 self._op = OperatorId() 

677 self._type = typ 

678 self._owner = None 

679 self._func = None 

680 

681 @property 

682 def ShortName(self): 

683 """ 

684 a short name (tells the column type) 

685 """ 

686 return "group" 

687 

688 def set_none(self): 

689 """ 

690 after a loop on a database, we should put None back as a value 

691 """ 

692 self._value = None 

693 

694 def __call__(self): 

695 """ 

696 returns the content 

697 """ 

698 if isinstance(self._value, GroupByContainer): 

699 try: 

700 return self._opgr(self._value) 

701 except TypeError as e: 

702 raise IterException( 

703 "unable(1) to apply an operator for column op=<{0}>, col={1}, TYPE={2} TYPE_OP={3}".format( 

704 str( 

705 self._op), str(self), type(self), type( 

706 self._op))) from e 

707 except AttributeError as ee: 

708 raise IterException( 

709 "unable(2) to apply an operator for column op=<{0}>, col={1}, TYPE={2} TYPE_OP={3}".format( 

710 str( 

711 self._op), str(self), type(self), type( 

712 self._op))) from ee 

713 else: 

714 return super().__call__() 

715 

716 def __str__(self): 

717 """ 

718 usual 

719 """ 

720 return "CGT[{0}]({1})".format(str(self._opgr), self._name) 

721 

722 def set(self, value): 

723 """ 

724 sets a value for this column 

725 

726 @param value anything in [int,float,long,str, function ] 

727 """ 

728 self._value = value 

729 if hasattr(value, "__iter__") and \ 

730 not isinstance(value, str) and \ 

731 not isinstance(value, GroupByContainer): 

732 raise IterException( 

733 "type of value should be GroupByContainer not {0} for the column {1}".format( 

734 type(value), 

735 str(self))) 

736 

737 def __mul__(self, column): 

738 """ 

739 forbidden 

740 """ 

741 raise NotAllowedOperation() 

742 

743 def __add__(self, column): 

744 """ 

745 forbidden 

746 """ 

747 raise NotAllowedOperation() 

748 

749 def __sub__(self, column): 

750 """ 

751 forbidden 

752 """ 

753 raise NotAllowedOperation() 

754 

755 def __truediv__(self, column): 

756 """ 

757 forbidden 

758 """ 

759 raise NotAllowedOperation() 

760 

761 def __floordiv__(self, column): 

762 """ 

763 forbidden 

764 """ 

765 raise NotAllowedOperation() 

766 

767 def __mod__(self, column): 

768 """ 

769 forbidden 

770 """ 

771 raise NotAllowedOperation() 

772 

773 def __pow__(self, column): 

774 """ 

775 forbidden 

776 """ 

777 raise NotAllowedOperation() 

778 

779 

780class CFT(ColumnType): 

781 

782 """ 

783 defines a function 

784 """ 

785 

786 def __init__(self, func, *args): 

787 """ 

788 constructor (a function cannot accept keywords) 

789 

790 @param func contained function 

791 @param args list of @see cl ColumnType 

792 """ 

793 self._name = None 

794 self._func = None 

795 self._parent = None 

796 self._op = OperatorFunc(func) 

797 self._type = type(private_function_type) 

798 self._owner = None 

799 self._thisfunc = func 

800 self._parent = tuple(args) 

801 

802 for _ in args: 

803 if not isinstance(_, ColumnType): 

804 raise TypeError( 

805 "Expecting a column type, not {}".format(type(_))) 

806 

807 @property 

808 def ShortName(self): 

809 """ 

810 a short name (tells the column type) 

811 """ 

812 return "func" 

813 

814 def set_none(self): 

815 """ 

816 after a loop on a database, we should put None back as a value 

817 """ 

818 self._value = None 

819 

820 def __str__(self): 

821 """ 

822 usual 

823 """ 

824 return "func({0},{1})".format( 

825 self._name, ColumnType._str_type[self._type])