@@ -648,33 +648,125 @@ define([
648648$ ( document ) . off ( 'change' , this . wrapSelector ( '.vp-inner-popup-var2' ) ) ;
649649}
650650
651- bindEventForPopupPage ( ) {
651+ bindEventForPopupPage ( menuType ) {
652652var that = this ;
653- ///// add page
654- // 1. add type
655- $ ( this . wrapSelector ( '.vp-inner-popup-addtype' ) ) . on ( 'change' , function ( ) {
656- var tab = $ ( this ) . val ( ) ;
657- $ ( that . wrapSelector ( '.vp-inner-popup-tab' ) ) . hide ( ) ;
658- $ ( that . wrapSelector ( '.vp-inner-popup-tab.' + tab ) ) . show ( ) ;
659- } ) ;
660-
661- // 2-1. hide column selection box
662- $ ( this . wrapSelector ( '.vp-inner-popup-var1box .vp-vs-data-type' ) ) . on ( 'change' , function ( ) {
663- var type = $ ( this ) . val ( ) ;
664- if ( type == 'DataFrame' ) {
665- $ ( that . wrapSelector ( '.vp-inner-popup-var1col' ) ) . show ( ) ;
666- } else {
667- $ ( that . wrapSelector ( '.vp-inner-popup-var1col' ) ) . hide ( ) ;
668- }
669- } ) ;
670-
671- $ ( this . wrapSelector ( '.vp-inner-popup-var2box .vp-vs-data-type' ) ) . on ( 'change' , function ( ) {
672- var type = $ ( this ) . val ( ) ;
673- if ( type == 'DataFrame' ) {
674- $ ( that . wrapSelector ( '.vp-inner-popup-var2col' ) ) . show ( ) ;
675- } else {
676- $ ( that . wrapSelector ( '.vp-inner-popup-var2col' ) ) . hide ( ) ;
677- }
653+
654+ if ( menuType === FRAME_EDIT_TYPE . ADD_COL
655+ || menuType === FRAME_EDIT_TYPE . ADD_ROW
656+ || menuType === FRAME_EDIT_TYPE . REPLACE ) {
657+ ///// add page
658+ // 1. add type
659+ $ ( this . wrapSelector ( '.vp-inner-popup-addtype' ) ) . on ( 'change' , function ( ) {
660+ var tab = $ ( this ) . val ( ) ;
661+ $ ( that . wrapSelector ( '.vp-inner-popup-tab' ) ) . hide ( ) ;
662+ $ ( that . wrapSelector ( '.vp-inner-popup-tab.' + tab ) ) . show ( ) ;
663+ } ) ;
664+
665+ // 2-1. hide column selection box
666+ $ ( this . wrapSelector ( '.vp-inner-popup-var1box .vp-vs-data-type' ) ) . on ( 'change' , function ( ) {
667+ var type = $ ( this ) . val ( ) ;
668+ if ( type == 'DataFrame' ) {
669+ $ ( that . wrapSelector ( '.vp-inner-popup-var1col' ) ) . show ( ) ;
670+ } else {
671+ $ ( that . wrapSelector ( '.vp-inner-popup-var1col' ) ) . hide ( ) ;
672+ }
673+ } ) ;
674+
675+ $ ( this . wrapSelector ( '.vp-inner-popup-var2box .vp-vs-data-type' ) ) . on ( 'change' , function ( ) {
676+ var type = $ ( this ) . val ( ) ;
677+ if ( type == 'DataFrame' ) {
678+ $ ( that . wrapSelector ( '.vp-inner-popup-var2col' ) ) . show ( ) ;
679+ } else {
680+ $ ( that . wrapSelector ( '.vp-inner-popup-var2col' ) ) . hide ( ) ;
681+ }
682+ } ) ;
683+ } else if ( menuType === FRAME_EDIT_TYPE . DISCRETIZE ) {
684+ // change bins
685+ $ ( this . wrapSelector ( '.vp-inner-popup-bins' ) ) . on ( 'change' , function ( ) {
686+ let binsCount = $ ( this ) . val ( ) ;
687+ that . handleDiscretizeEdges ( binsCount ) ;
688+ } ) ;
689+
690+ // change cut to qcut(quantile based discretization)
691+ $ ( this . wrapSelector ( '.vp-inner-popup-qcut' ) ) . on ( 'change' , function ( ) {
692+ let qcut = $ ( this ) . prop ( 'checked' ) ;
693+ // disable right and range table
694+ if ( qcut === true ) {
695+ $ ( that . wrapSelector ( '.vp-inner-popup-right' ) ) . prop ( 'disabled' , true ) ;
696+ $ ( that . wrapSelector ( '.vp-inner-popup-range-table input:not(.vp-inner-popup-label):disabled' ) ) . addClass ( 'already-disabled' ) ;
697+ $ ( that . wrapSelector ( '.vp-inner-popup-range-table input:not(.vp-inner-popup-label)' ) ) . prop ( 'disabled' , true ) ;
698+ } else {
699+ $ ( that . wrapSelector ( '.vp-inner-popup-right' ) ) . prop ( 'disabled' , false ) ;
700+ $ ( that . wrapSelector ( '.vp-inner-popup-range-table input:not(.vp-inner-popup-label):not(.already-disabled)' ) ) . prop ( 'disabled' , false ) ;
701+ $ ( that . wrapSelector ( '.vp-inner-popup-range-table input:not(.vp-inner-popup-label).already-disabled' ) ) . removeClass ( 'already-disabled' ) ;
702+ }
703+ } ) ;
704+
705+ // change right option
706+ $ ( this . wrapSelector ( '.vp-inner-popup-right' ) ) . on ( 'change' , function ( ) {
707+ let binsCount = $ ( that . wrapSelector ( '.vp-inner-popup-bins' ) ) . val ( ) ;
708+ let right = $ ( this ) . prop ( 'checked' ) ;
709+ that . handleDiscretizeEdges ( binsCount , right ) ;
710+ } ) ;
711+ }
712+
713+ }
714+
715+ handleDiscretizeEdges ( binsCount = 1 , right = true ) {
716+ let that = this ;
717+ $ ( this . wrapSelector ( '.vp-inner-popup-range-table tbody' ) ) . html ( '' ) ;
718+ $ ( this . wrapSelector ( '.vp-inner-popup-islabelchanged' ) ) . val ( "false" ) ;
719+ $ ( that . wrapSelector ( '.vp-inner-popup-isedgechanged' ) ) . val ( "false" ) ;
720+
721+ let code = new com_String ( ) ;
722+ code . appendFormatLine ( "_out, _bins = pd.cut({0}[{1}], bins={2}, right={3}, retbins=True)"
723+ , this . state . tempObj , this . state . selected [ 0 ] . code , binsCount , right ?'True' :'False' ) ;
724+ code . append ( "_vp_print({'labels': [str(o) for o in _out.cat.categories], 'edges': list(_bins)})" ) ;
725+ vpKernel . execute ( code . toString ( ) ) . then ( function ( resultObj ) {
726+ let { result} = resultObj ;
727+ let { labels, edges} = JSON . parse ( result ) ;
728+
729+ let edgeTbody = new com_String ( ) ;
730+ labels && labels . forEach ( ( label , idx ) => {
731+ let leftDisabled = 'disabled' ;
732+ let rightDisabled = '' ;
733+ if ( idx === ( labels . length - 1 ) ) {
734+ rightDisabled = 'disabled' ;
735+ }
736+ if ( right === false ) {
737+ [ leftDisabled , rightDisabled ] = [ rightDisabled , leftDisabled ] ;
738+ }
739+ edgeTbody . append ( '<tr>' ) ;
740+ edgeTbody . appendFormatLine ( '<td><input type="text" class="vp-input m vp-inner-popup-label" data-idx="{0}" value="{1}"/></td>' , idx , label ) ;
741+ edgeTbody . appendLine ( '<td>:</td>' ) ;
742+ edgeTbody . appendFormatLine ( '<td><input type="number" class="vp-input m vp-inner-popup-left-edge" data-idx="{0}" value="{1}" {2}/></td>' , idx , edges [ idx ] , leftDisabled ) ;
743+ edgeTbody . appendLine ( '<td>~</td>' ) ;
744+ edgeTbody . appendFormatLine ( '<td><input type="number" class="vp-input m vp-inner-popup-right-edge" data-idx="{0}" value="{1}" {2}/></td>' , idx + 1 , edges [ idx + 1 ] , rightDisabled ) ;
745+ edgeTbody . append ( '</tr>' ) ;
746+ } ) ;
747+ $ ( that . wrapSelector ( '.vp-inner-popup-range-table tbody' ) ) . html ( edgeTbody . toString ( ) ) ;
748+
749+ // label change event
750+ $ ( that . wrapSelector ( '.vp-inner-popup-label' ) ) . change ( function ( ) {
751+ $ ( that . wrapSelector ( '.vp-inner-popup-islabelchanged' ) ) . val ( "true" ) ;
752+ } ) ;
753+
754+ // edge change event
755+ $ ( that . wrapSelector ( '.vp-inner-popup-left-edge' ) ) . change ( function ( ) {
756+ let idx = $ ( this ) . data ( 'idx' ) ;
757+ let val = $ ( this ) . val ( ) ;
758+ $ ( that . wrapSelector ( `.vp-inner-popup-right-edge[data-idx=${ idx } ]` ) ) . val ( val ) ;
759+ $ ( that . wrapSelector ( '.vp-inner-popup-isedgechanged' ) ) . val ( "true" ) ;
760+ } ) ;
761+ $ ( that . wrapSelector ( '.vp-inner-popup-right-edge' ) ) . change ( function ( ) {
762+ let idx = $ ( this ) . data ( 'idx' ) ;
763+ let val = $ ( this ) . val ( ) ;
764+ $ ( that . wrapSelector ( `.vp-inner-popup-left-edge[data-idx=${ idx } ]` ) ) . val ( val ) ;
765+ $ ( that . wrapSelector ( '.vp-inner-popup-isedgechanged' ) ) . val ( "true" ) ;
766+ } ) ;
767+
768+ } ) . catch ( function ( errObj ) {
769+ // TODO:
678770} ) ;
679771}
680772
@@ -1014,6 +1106,53 @@ define([
10141106return content . toString ( ) ;
10151107}
10161108
1109+ renderDiscretizePage ( ) {
1110+ var content = new com_String ( ) ;
1111+ content . appendLine ( `
1112+ <div class="vp-inner-popup-discretize-page vp-grid-box">
1113+ <div class="vp-grid-col-110">
1114+ <label class="vp-orange-text">Column name</label>
1115+ <input type="text" class="vp-input" value="${ this . state . selected [ 0 ] . label } " disabled />
1116+ <label>Bins count</label>
1117+ <input type="number" class="vp-input vp-inner-popup-bins" placeholder="Input count of bins"/>
1118+ </div>
1119+ <label title="pd.qcut() option">
1120+ <input type="checkbox" class="vp-inner-popup-qcut">
1121+ <span>Quantile-based discretization</span>
1122+ </label>
1123+ <label title="right option">
1124+ <input type="checkbox" class="vp-inner-popup-right" checked>
1125+ <span>Include the rightmost edge</span>
1126+ </label>
1127+ <hr style="margin: 5px 0;"/>
1128+ <table class="vp-tbl-gap5 vp-inner-popup-range-table">
1129+ <colgroup><col width="116px"><col width="5px"><col width="116px"><col width="5px"><col width="*"></colgroup>
1130+ <thead>
1131+ <tr>
1132+ <th>Label</th>
1133+ <th></th>
1134+ <th>Left edge</th>
1135+ <th></th>
1136+ <th>Right edge</th>
1137+ </tr>
1138+ </thead>
1139+ <tbody>
1140+ </tbody>
1141+ </table>
1142+ <label title="Set all labels as text">
1143+ <input type="checkbox" class="vp-inner-popup-labelastext" checked>
1144+ <span>Label as Text</span>
1145+ </label>
1146+ <input type="hidden" class="vp-inner-popup-islabelchanged" value=false />
1147+ <input type="hidden" class="vp-inner-popup-isedgechanged" value=false />
1148+ </div>
1149+ ` )
1150+
1151+ // set content
1152+ $ ( this . wrapSelector ( '.vp-inner-popup-body' ) ) . html ( content . toString ( ) ) ;
1153+ return content . toString ( ) ;
1154+ }
1155+
10171156renderSortPage ( ) {
10181157var content = new com_String ( ) ;
10191158content . appendFormatLine ( '<div class="{0}">' , 'vp-inner-popup-sort-page' ) ;
@@ -1152,6 +1291,16 @@ define([
11521291title = 'Rename' ;
11531292content = this . renderRenamePage ( ) ;
11541293break ;
1294+ case FRAME_EDIT_TYPE . DISCRETIZE :
1295+ title = 'Discretize' ;
1296+ size = { width :450 , height :450 } ;
1297+ content = this . renderDiscretizePage ( ) ;
1298+ break ;
1299+ case FRAME_EDIT_TYPE . DATA_SHIFT :
1300+ title = 'Data shift' ;
1301+ // TODO:
1302+ content = 'WIP' ;
1303+ break ;
11551304case FRAME_EDIT_TYPE . SORT_INDEX :
11561305title = 'Sort by index' ;
11571306content = this . renderSortPage ( ) ;
@@ -1206,6 +1355,11 @@ define([
12061355title = 'Convert type' ;
12071356content = this . renderAsType ( ) ;
12081357break ;
1358+ case FRAME_EDIT_TYPE . FILL_NA :
1359+ title = 'Fill NA' ;
1360+ // TODO:
1361+ content = 'WIP' ;
1362+ break ;
12091363default :
12101364type = FRAME_EDIT_TYPE . NONE ;
12111365break ;
@@ -1217,7 +1371,7 @@ define([
12171371$ ( this . wrapSelector ( '.vp-inner-popup-box' ) ) . css ( size ) ;
12181372
12191373// bindEventForAddPage
1220- this . bindEventForPopupPage ( ) ;
1374+ this . bindEventForPopupPage ( type ) ;
12211375
12221376// set column list
12231377vpKernel . getColumnList ( this . state . tempObj ) . then ( function ( resultObj ) {
@@ -1344,6 +1498,30 @@ define([
13441498} ;
13451499} ) ;
13461500break ;
1501+ case FRAME_EDIT_TYPE . DISCRETIZE :
1502+ content [ 'bins' ] = $ ( this . wrapSelector ( '.vp-inner-popup-bins' ) ) . val ( ) ;
1503+ content [ 'isqcut' ] = $ ( this . wrapSelector ( '.vp-inner-popup-qcut' ) ) . prop ( 'checked' ) ;
1504+ content [ 'isright' ] = $ ( this . wrapSelector ( '.vp-inner-popup-right' ) ) . prop ( 'checked' ) ;
1505+ let labelastext = $ ( this . wrapSelector ( '.vp-inner-popup-labelastext' ) ) . prop ( 'checked' ) ;
1506+ let islabelchanged = $ ( this . wrapSelector ( '.vp-inner-popup-islabelchanged' ) ) . val ( ) === 'true' ;
1507+ let isedgechanged = $ ( this . wrapSelector ( '.vp-inner-popup-isedgechanged' ) ) . val ( ) === 'true' ;
1508+ let rangeTableTags = $ ( this . wrapSelector ( '.vp-inner-popup-range-table tbody tr' ) ) ;
1509+ let labels = [ ] ;
1510+ let edges = [ ] ;
1511+ rangeTableTags && rangeTableTags . each ( ( idx , tag ) => {
1512+ if ( islabelchanged === true ) {
1513+ labels . push ( com_util . convertToStr ( $ ( tag ) . find ( '.vp-inner-popup-label' ) . val ( ) , labelastext ) ) ;
1514+ }
1515+ if ( content [ 'isqcut' ] === false && isedgechanged === true ) {
1516+ edges . push ( $ ( tag ) . find ( '.vp-inner-popup-left-edge' ) . val ( ) ) ;
1517+ if ( idx === ( rangeTableTags . length - 1 ) ) {
1518+ edges . push ( $ ( tag ) . find ( '.vp-inner-popup-right-edge' ) . val ( ) ) ;
1519+ }
1520+ }
1521+ } ) ;
1522+ content [ 'labels' ] = labels ;
1523+ content [ 'edges' ] = edges ;
1524+ break ;
13471525default :
13481526break ;
13491527}
@@ -1601,6 +1779,28 @@ define([
16011779} ) ;
16021780code . appendFormat ( "{0} = {1}.astype({{2}})" , tempObj , tempObj , astypeStr . toString ( ) ) ;
16031781break ;
1782+ case FRAME_EDIT_TYPE . DISCRETIZE :
1783+ let method = 'cut' ;
1784+ let bins = content [ 'bins' ] ;
1785+ if ( content [ 'isqcut' ] === true ) {
1786+ method = 'qcut' ;
1787+ } else {
1788+ if ( content [ 'isedgechanged' ] === true ) {
1789+ bins = "[" + content [ 'edges' ] . join ( ',' ) + "]" ;
1790+ }
1791+ }
1792+
1793+ code . appendFormat ( "{0}[{1}] = pd.{2}({3}[{4}], {5}"
1794+ , tempObj , selectedName , method , tempObj , selectedName , bins ) ;
1795+
1796+ if ( method === 'cut' && content [ 'isright' ] === false ) {
1797+ code . append ( ", right=False" ) ;
1798+ }
1799+ if ( content [ 'labels' ] && content [ 'labels' ] . length > 0 ) {
1800+ code . appendFormat ( ", labels=[{0}]" , content [ 'labels' ] . join ( ', ' ) ) ;
1801+ }
1802+ code . append ( ')' ) ;
1803+ break ;
16041804case FRAME_EDIT_TYPE . SHOW :
16051805break ;
16061806}