23
23
+ ["ids" , "error_x" , "error_x_minus" , "error_y" , "error_y_minus" , "error_z" ]
24
24
+ ["error_z_minus" , "lat" , "lon" , "locations" , "animation_group" ]
25
25
)
26
- array_attrables = ["dimensions" , "custom_data" , "hover_data" , "path" , "wide_cols " ]
26
+ array_attrables = ["dimensions" , "custom_data" , "hover_data" , "path" , "_column_ " ]
27
27
group_attrables = ["animation_frame" , "facet_row" , "facet_col" , "line_group" ]
28
28
renameable_group_attrables = [
29
29
"color" , # renamed to marker.color or line.color in infer_config
@@ -913,6 +913,27 @@ def _get_reserved_col_names(args):
913
913
return reserved_names
914
914
915
915
916
+ def _is_col_list (df_input , arg ):
917
+ if arg is None or isinstance (arg , str ) or isinstance (arg , int ):
918
+ return False
919
+ if isinstance (arg , pd .MultiIndex ):
920
+ return False # just to keep existing behaviour for now
921
+ try :
922
+ iter (arg )
923
+ except TypeError :
924
+ return False # not iterable
925
+ for c in arg :
926
+ if isinstance (c , str ) or isinstance (c , int ):
927
+ if df_input is None or c not in df_input .columns :
928
+ return False
929
+ else :
930
+ try :
931
+ iter (c )
932
+ except TypeError :
933
+ return False # not iterable
934
+ return True
935
+
936
+
916
937
def build_dataframe (args , constructor ):
917
938
"""
918
939
Constructs a dataframe and modifies `args` in-place.
@@ -946,60 +967,60 @@ def build_dataframe(args, constructor):
946
967
947
968
no_x = args .get ("x" , None ) is None
948
969
no_y = args .get ("y" , None ) is None
949
- wideable = [go .Scatter , go .Bar , go .Violin , go .Box , go .Histogram ]
950
- wide_mode = df_provided and no_x and no_y and constructor in wideable
951
- wide_id_vars = set ()
970
+ wide_x = False if no_x else _is_col_list (df_input , args ["x" ])
971
+ wide_y = False if no_y else _is_col_list (df_input , args ["y" ])
952
972
953
- if wide_mode :
954
- # currently assuming that df_provided == True
955
- args ["wide_cols" ] = list (df_input .columns )
956
- args ["wide_cross" ] = df_input .index
957
- var_name = df_input .columns .name or "_column_"
958
- wide_orientation = args .get ("orientation" , None ) or "v"
959
- args ["orientation" ] = wide_orientation
973
+ wide_mode = False
974
+ if constructor in [go .Scatter , go .Bar , go .Violin , go .Box , go .Histogram ]:
975
+ wide_cross_name = None
976
+ if wide_x and wide_y :
977
+ raise ValueError (
978
+ "Cannot accept list of column references or list of columns for both `x` and `y`."
979
+ )
980
+ if df_provided and no_x and no_y :
981
+ wide_mode = True
982
+ args ["_column_" ] = list (df_input .columns )
983
+ var_name = df_input .columns .name or "_column_"
984
+ wide_orientation = args .get ("orientation" , None ) or "v"
985
+ args ["orientation" ] = wide_orientation
986
+ args ["wide_cross" ] = None
987
+ elif wide_x != wide_y :
988
+ wide_mode = True
989
+ args ["_column_" ] = args ["y" ] if wide_y else args ["x" ]
990
+ var_name = "_column_"
991
+ if constructor == go .Histogram :
992
+ wide_orientation = "v" if wide_x else "h"
993
+ else :
994
+ wide_orientation = "v" if wide_y else "h"
995
+ args ["y" if wide_y else "x" ] = None
996
+ args ["wide_cross" ] = None
997
+ if not no_x and not no_y :
998
+ wide_cross_name = "__x__" if wide_y else "__y__"
960
999
961
- """
962
- wide_x detection
963
- - if scalar = False
964
- - else if list of lists = True
965
- - else if not df_provided = False
966
- - else if contents are unique and are contained in columns = True
967
- - else = False
968
-
969
-
970
- wide detection:
971
- - if no_x and no_y = wide mode
972
- - else if wide_x and wide_y = error
973
- - else if wide_x xor wide_y = wide mode
974
- - else = long mode
975
-
976
- so what we want is:
977
- - y = [col col] -> melt just those, wide_orientation = 'v'/no override, cross_dim = index or range
978
- - y = [col col] / x=col -> wide_orientation = 'h'/no override, cross_dim = x
979
- - y = [col col] / x=[col col] -> error
980
-
981
- need to merge wide logic into no_x/no_y logic below for range() etc
982
- """
1000
+ missing_bar_dim = None
1001
+ if constructor in [go .Scatter , go .Bar ]:
1002
+ if not wide_mode and (no_x != no_y ):
1003
+ for ax in ["x" , "y" ]:
1004
+ if args .get (ax , None ) is None :
1005
+ args [ax ] = df_input .index if df_provided else Range ()
1006
+ if constructor == go .Scatter :
1007
+ if args ["orientation" ] is None :
1008
+ args ["orientation" ] = "v" if ax == "x" else "h"
1009
+ if constructor == go .Bar :
1010
+ missing_bar_dim = ax
1011
+ if wide_mode and wide_cross_name is None :
1012
+ if df_provided :
1013
+ args ["wide_cross" ] = df_input .index
1014
+ wide_cross_name = df_input .index .name or "index"
1015
+ else :
1016
+ args ["wide_cross" ] = Range (label = "index" )
1017
+ wide_cross_name = "index"
983
1018
984
1019
df_output = pd .DataFrame ()
985
-
986
- missing_bar_dim = None
987
- if constructor in [go .Scatter , go .Bar ] and (no_x != no_y ):
988
- for ax in ["x" , "y" ]:
989
- if args .get (ax , None ) is None :
990
- args [ax ] = df_input .index if df_provided else Range ()
991
- if constructor == go .Scatter :
992
- if args ["orientation" ] is None :
993
- args ["orientation" ] = "v" if ax == "x" else "h"
994
- if constructor == go .Bar :
995
- missing_bar_dim = ax
996
-
997
- # Initialize set of column names
998
- # These are reserved names
999
- if df_provided :
1000
- reserved_names = _get_reserved_col_names (args )
1001
- else :
1002
- reserved_names = set ()
1020
+ constants = dict ()
1021
+ ranges = list ()
1022
+ wide_id_vars = set ()
1023
+ reserved_names = _get_reserved_col_names (args ) if df_provided else set ()
1003
1024
1004
1025
# Case of functions with a "dimensions" kw: scatter_matrix, parcats, parcoords
1005
1026
if "dimensions" in args and args ["dimensions" ] is None :
@@ -1010,8 +1031,6 @@ def build_dataframe(args, constructor):
1010
1031
else :
1011
1032
df_output [df_input .columns ] = df_input [df_input .columns ]
1012
1033
1013
- constants = dict ()
1014
- ranges = list ()
1015
1034
1016
1035
# Loop over possible arguments
1017
1036
for field_name in all_attrables :
@@ -1136,10 +1155,10 @@ def build_dataframe(args, constructor):
1136
1155
args [field_name ] = str (col_name )
1137
1156
else :
1138
1157
args [field_name ][i ] = str (col_name )
1139
- if field_name != "wide_cols " :
1158
+ if field_name != "_column_ " :
1140
1159
wide_id_vars .add (str (col_name ))
1141
1160
1142
- if missing_bar_dim and constructor == go .Bar :
1161
+ if not wide_mode and missing_bar_dim and constructor == go .Bar :
1143
1162
# now that we've populated df_output, we check to see if the non-missing
1144
1163
# dimension is categorical: if so, then setting the missing dimension to a
1145
1164
# constant 1 is a less-insane thing to do than setting it to the index by
@@ -1161,9 +1180,8 @@ def build_dataframe(args, constructor):
1161
1180
df_output [col_name ] = constants [col_name ]
1162
1181
1163
1182
if wide_mode :
1164
- wide_value_vars = [c for c in args ["wide_cols" ] if c not in wide_id_vars ]
1165
- del args ["wide_cols" ]
1166
- wide_cross = args ["wide_cross" ]
1183
+ wide_value_vars = [c for c in args ["_column_" ] if c not in wide_id_vars ]
1184
+ del args ["_column_" ]
1167
1185
del args ["wide_cross" ]
1168
1186
df_output = df_output .melt (
1169
1187
id_vars = wide_id_vars ,
@@ -1173,14 +1191,18 @@ def build_dataframe(args, constructor):
1173
1191
)
1174
1192
df_output [var_name ] = df_output [var_name ].astype (str )
1175
1193
orient_v = wide_orientation == "v"
1194
+ if wide_cross_name == "__x__" :
1195
+ wide_cross_name = args ["x" ]
1196
+ if wide_cross_name == "__y__" :
1197
+ wide_cross_name = args ["y" ]
1176
1198
1177
1199
if constructor == go .Scatter :
1178
- args ["x" if orient_v else "y" ] = wide_cross
1200
+ args ["x" if orient_v else "y" ] = wide_cross_name
1179
1201
args ["y" if orient_v else "x" ] = "_value_"
1180
1202
args ["color" ] = args ["color" ] or var_name
1181
1203
if constructor == go .Bar :
1182
1204
if _is_continuous (df_output , "_value_" ):
1183
- args ["x" if orient_v else "y" ] = wide_cross
1205
+ args ["x" if orient_v else "y" ] = wide_cross_name
1184
1206
args ["y" if orient_v else "x" ] = "_value_"
1185
1207
args ["color" ] = args ["color" ] or var_name
1186
1208
else :
@@ -1189,10 +1211,11 @@ def build_dataframe(args, constructor):
1189
1211
df_output ["_count_" ] = 1
1190
1212
args ["color" ] = args ["color" ] or var_name
1191
1213
if constructor in [go .Violin , go .Box ]:
1192
- args ["x" if orient_v else "y" ] = var_name
1214
+ args ["x" if orient_v else "y" ] = wide_cross_name or var_name
1193
1215
args ["y" if orient_v else "x" ] = "_value_"
1194
1216
if constructor == go .Histogram :
1195
1217
args ["x" if orient_v else "y" ] = "_value_"
1218
+ args ["y" if orient_v else "x" ] = wide_cross_name
1196
1219
args ["color" ] = args ["color" ] or var_name
1197
1220
1198
1221
args ["data_frame" ] = df_output
0 commit comments