9
9
10
10
import numpy as np
11
11
import pandas as pd
12
+ from bqplot .traits import array_from_json , array_to_json
12
13
from ipywidgets import CallbackDispatcher , DOMWidget , widget_serialization
13
14
from traitlets import (
14
15
Bool ,
@@ -76,7 +77,6 @@ def _cell_in_rect(cell, rect):
76
77
77
78
78
79
class SelectionHelper :
79
-
80
80
"""A Helper Class for processing selections. Provides an iterator
81
81
to traverse selected cells.
82
82
"""
@@ -164,14 +164,13 @@ def _get_num_rows(self):
164
164
return self ._num_rows
165
165
166
166
167
- # modified from ipywidgets original
168
- def _data_to_json (x ):
167
+ def _data_to_json (x , _ ):
169
168
if isinstance (x , dict ):
170
- return {str (k ): _data_to_json (v ) for k , v in x .items ()}
169
+ return {str (k ): _data_to_json (v , _ ) for k , v in x .items ()}
171
170
if isinstance (x , np .ndarray ):
172
- return _data_to_json (x .tolist ())
171
+ return _data_to_json (x .tolist (), _ )
173
172
if isinstance (x , (list , tuple )):
174
- return [_data_to_json (v ) for v in x ]
173
+ return [_data_to_json (v , _ ) for v in x ]
175
174
if isinstance (x , int ):
176
175
return x
177
176
if isinstance (x , float ):
@@ -193,9 +192,55 @@ def _data_to_json(x):
193
192
return str (x )
194
193
195
194
195
+ def _data_serialization_impl (data , _ ):
196
+ if not data :
197
+ return {}
198
+
199
+ serialized_data = {}
200
+ for column , value in data ["data" ].items ():
201
+ arr = value .to_numpy ()
202
+ if arr .size == 0 :
203
+ serialized_data [str (column )] = {
204
+ "value" : [],
205
+ "dtype" : str (arr .dtype ),
206
+ "shape" : arr .shape ,
207
+ "type" : None ,
208
+ }
209
+ continue
210
+ try :
211
+ serialized_data [str (column )] = array_to_json (arr )
212
+ except ValueError :
213
+ # Column is most likely heterogeneous, sending the column raw
214
+ serialized_data [str (column )] = {
215
+ "value" : _data_to_json (arr , _ ),
216
+ "type" : "raw" ,
217
+ }
218
+
219
+ return {
220
+ "data" : serialized_data ,
221
+ "schema" : data ["schema" ],
222
+ "fields" : _data_to_json (data ["fields" ], _ ),
223
+ }
224
+
225
+
226
+ def _data_deserialization_impl (data , _ ): # noqa: U101
227
+ if not data :
228
+ return {}
229
+
230
+ deserialized_data = {}
231
+ for column , value in data ["data" ].items ():
232
+ deserialized_data [column ] = array_from_json (value .to_numpy ())
233
+
234
+ return {
235
+ "data" : deserialized_data ,
236
+ "schema" : data ["schema" ],
237
+ "fields" : data ["fields" ],
238
+ }
239
+
240
+
196
241
_data_serialization = {
197
- "from_json" : widget_serialization [ "from_json" ] ,
198
- "to_json" : lambda x , _ : _data_to_json ( x ), # noqa: U101
242
+ "from_json" : _data_deserialization_impl ,
243
+ "to_json" : _data_serialization_impl ,
199
244
}
200
245
201
246
@@ -212,7 +257,6 @@ def _widgets_dict_to_json(x, obj):
212
257
213
258
214
259
class DataGrid (DOMWidget ):
215
-
216
260
"""A Grid Widget with filter, sort and selection capabilities.
217
261
218
262
Attributes
@@ -360,7 +404,7 @@ class DataGrid(DOMWidget):
360
404
).tag (sync = True )
361
405
selections = List (Dict ()).tag (sync = True )
362
406
editable = Bool (False ).tag (sync = True )
363
- column_widths = Dict ({}).tag (sync = True , ** _data_serialization )
407
+ column_widths = Dict ({}).tag (sync = True , to_json = _data_to_json )
364
408
grid_style = Dict (allow_none = True ).tag (
365
409
sync = True , ** _widgets_dict_serialization
366
410
)
@@ -383,17 +427,15 @@ def __init__(self, dataframe, index_name=None, **kwargs):
383
427
def __handle_custom_msg (self , _ , content , buffers ): # noqa: U101,U100
384
428
if content ["event_type" ] == "cell-changed" :
385
429
row = content ["row" ]
386
- column = self ._column_index_to_name (
387
- self ._data , content ["column_index" ]
388
- )
430
+ column = content ["column" ]
389
431
value = content ["value" ]
390
432
# update data on kernel
391
- self ._data ["data" ][row ][ column ] = value
433
+ self ._data ["data" ]. loc [row , column ] = value
392
434
# notify python listeners
393
435
self ._cell_change_handlers (
394
436
{
395
437
"row" : row ,
396
- "column" : column ,
438
+ "column" : content [ " column" ] ,
397
439
"column_index" : content ["column_index" ],
398
440
"value" : value ,
399
441
}
@@ -414,7 +456,7 @@ def __handle_custom_msg(self, _, content, buffers): # noqa: U101,U100
414
456
@property
415
457
def data (self ):
416
458
trimmed_primary_key = self ._data ["schema" ]["primaryKey" ][:- 1 ]
417
- if self . _data [ "data" ] :
459
+ if "data" in self . _data :
418
460
df = pd .DataFrame (self ._data ["data" ])
419
461
else :
420
462
df = pd .DataFrame (
@@ -460,7 +502,7 @@ def generate_data_object(dataframe, guid_key="ipydguuid", index_name="key"):
460
502
461
503
schema = pd .io .json .build_table_schema (dataframe )
462
504
reset_index_dataframe = dataframe .reset_index ()
463
- data = reset_index_dataframe . to_dict ( orient = "records" )
505
+ data = reset_index_dataframe
464
506
465
507
# Check for multiple primary keys
466
508
key = reset_index_dataframe .columns [: dataframe .index .nlevels ].tolist ()
@@ -522,7 +564,7 @@ def get_cell_value(self, column_name, primary_key_value):
522
564
if isinstance (column_name , list ):
523
565
column_name = tuple (column_name )
524
566
525
- return [self ._data ["data" ][row ][ column_name ] for row in row_indices ]
567
+ return [self ._data ["data" ][column_name ][ row ] for row in row_indices ]
526
568
527
569
def set_cell_value (self , column_name , primary_key_value , new_value ):
528
570
"""Sets the value for a single cell by column name and primary key.
@@ -541,9 +583,9 @@ def set_cell_value(self, column_name, primary_key_value, new_value):
541
583
# Iterate over all indices
542
584
outcome = True
543
585
for row_index in row_indices :
544
- has_column = column_name in self ._data ["data" ][ row_index ]
586
+ has_column = column_name in self ._data ["data" ]
545
587
if has_column and row_index is not None :
546
- self ._data ["data" ][row_index ][ column_name ] = new_value
588
+ self ._data ["data" ]. loc [row_index , column_name ] = new_value
547
589
self ._notify_cell_change (row_index , column_name , new_value )
548
590
else :
549
591
outcome = False
@@ -565,7 +607,9 @@ def set_row_value(self, primary_key_value, new_value):
565
607
column_index = 0
566
608
column = DataGrid ._column_index_to_name (self ._data , column_index )
567
609
while column is not None :
568
- self ._data ["data" ][row_index ][column ] = new_value [column_index ]
610
+ self ._data ["data" ].loc [row_index , column ] = new_value [
611
+ column_index
612
+ ]
569
613
570
614
column_index = column_index + 1
571
615
column = DataGrid ._column_index_to_name (
@@ -577,17 +621,17 @@ def set_row_value(self, primary_key_value, new_value):
577
621
578
622
def get_cell_value_by_index (self , column_name , row_index ):
579
623
"""Gets the value for a single cell by column name and row index."""
580
- return self ._data ["data" ][row_index ][ column_name ]
624
+ return self ._data ["data" ][column_name ][ row_index ]
581
625
582
626
def set_cell_value_by_index (self , column_name , row_index , new_value ):
583
627
"""Sets the value for a single cell by column name and row index.
584
628
585
629
Note: This method returns a boolean to indicate if the operation
586
630
was successful.
587
631
"""
588
- has_column = column_name in self ._data ["data" ][ row_index ]
589
- if has_column and 0 <= row_index < len (self ._data ["data" ]):
590
- self ._data ["data" ][row_index ][ column_name ] = new_value
632
+ has_column = column_name in self ._data ["data" ]
633
+ if has_column and 0 <= row_index < len (self ._data ["data" ][ column_name ] ):
634
+ self ._data ["data" ]. loc [row_index , column_name ] = new_value
591
635
self ._notify_cell_change (row_index , column_name , new_value )
592
636
return True
593
637
return False
@@ -634,7 +678,7 @@ def get_visible_data(self):
634
678
"""Returns a dataframe of the current View."""
635
679
data = deepcopy (self ._data )
636
680
if self ._visible_rows :
637
- data ["data" ] = [ data ["data" ][ i ] for i in self ._visible_rows ]
681
+ data ["data" ] = data ["data" ]. reindex ( self ._visible_rows )
638
682
639
683
at = self ._data ["schema" ]["primaryKey" ]
640
684
return_df = pd .DataFrame (data ["data" ]).set_index (at )
@@ -852,9 +896,10 @@ def _get_row_index_of_primary_key(self, value):
852
896
"as the primary key."
853
897
)
854
898
899
+ # TODO Is there a better way for this?
855
900
row_indices = [
856
- at
857
- for at , row in enumerate ( self ._data ["data" ])
901
+ idx
902
+ for idx , row in self ._data ["data" ]. iterrows ( )
858
903
if all (row [key [j ]] == value [j ] for j in range (len (key )))
859
904
]
860
905
return row_indices
@@ -865,7 +910,7 @@ def _get_cell_value_by_numerical_index(data, column_index, row_index):
865
910
column = DataGrid ._column_index_to_name (data , column_index )
866
911
if column is None :
867
912
return None
868
- return data ["data" ][row_index ][ column ]
913
+ return data ["data" ]. loc [row_index , column ]
869
914
870
915
def _set_renderer_defaults (self ):
871
916
# Set sensible default values for renderers that are not completely
0 commit comments