@@ -899,8 +899,8 @@ def _check_name_not_reserved(field_name, reserved_names):
899
899
return field_name
900
900
else :
901
901
raise NameError (
902
- "A name conflict was encountered for argument %s . "
903
- "A column with name %s is already used ." % (field_name , field_name )
902
+ "A name conflict was encountered for argument '%s' . "
903
+ "A column or index with name '%s' is ambiguous ." % (field_name , field_name )
904
904
)
905
905
906
906
@@ -929,6 +929,8 @@ def _get_reserved_col_names(args):
929
929
in_df = arg is df [arg_name ]
930
930
if in_df :
931
931
reserved_names .add (arg_name )
932
+ elif arg is df .index and arg .name is not None :
933
+ reserved_names .add (arg .name )
932
934
933
935
return reserved_names
934
936
@@ -970,8 +972,8 @@ def _isinstance_listlike(x):
970
972
return True
971
973
972
974
973
- def _escape_col_name (df_input , col_name ):
974
- while df_input is not None and col_name in df_input .columns :
975
+ def _escape_col_name (df_input , col_name , extra ):
976
+ while df_input is not None and ( col_name in df_input .columns or col_name in extra ) :
975
977
col_name = "_" + col_name
976
978
return col_name
977
979
@@ -1040,6 +1042,7 @@ def process_args_into_dataframe(args, wide_mode, var_name, value_name):
1040
1042
length = len (df_output )
1041
1043
if argument is None :
1042
1044
continue
1045
+ col_name = None
1043
1046
# Case of multiindex
1044
1047
if isinstance (argument , pd .MultiIndex ):
1045
1048
raise TypeError (
@@ -1107,31 +1110,25 @@ def process_args_into_dataframe(args, wide_mode, var_name, value_name):
1107
1110
df_output [col_name ] = df_input [argument ].values
1108
1111
# ----------------- argument is a column / array / list.... -------
1109
1112
else :
1110
- is_index = isinstance (argument , pd .Index )
1111
- # First pandas
1112
- # pandas series have a name but it's None
1113
- if (
1114
- hasattr (argument , "name" ) and argument .name is not None
1115
- ) or is_index :
1116
- col_name = argument .name # pandas df
1117
- if col_name is None and is_index :
1118
- col_name = "index"
1119
- if not df_provided :
1120
- col_name = field
1121
- else :
1122
- if is_index :
1123
- keep_name = df_provided and argument is df_input .index
1113
+ if df_provided and hasattr (argument , "name" ):
1114
+ if argument is df_input .index :
1115
+ if argument .name is None or argument .name in df_input :
1116
+ col_name = "index"
1124
1117
else :
1125
- keep_name = (
1126
- col_name in df_input and argument is df_input [col_name ]
1127
- )
1128
- col_name = (
1129
- col_name
1130
- if keep_name
1131
- else _check_name_not_reserved (field , reserved_names )
1118
+ col_name = argument .name
1119
+ col_name = _escape_col_name (
1120
+ df_input , col_name , [var_name , value_name ]
1132
1121
)
1133
- else : # numpy array, list...
1122
+ else :
1123
+ if (
1124
+ argument .name is not None
1125
+ and argument .name in df_input
1126
+ and argument is df_input [argument .name ]
1127
+ ):
1128
+ col_name = argument .name
1129
+ if col_name is None : # numpy array, list...
1134
1130
col_name = _check_name_not_reserved (field , reserved_names )
1131
+
1135
1132
if length and len (argument ) != length :
1136
1133
raise ValueError (
1137
1134
"All arguments should have the same length. "
@@ -1145,6 +1142,12 @@ def process_args_into_dataframe(args, wide_mode, var_name, value_name):
1145
1142
df_output [str (col_name )] = np .array (argument )
1146
1143
1147
1144
# Finally, update argument with column name now that column exists
1145
+ assert col_name is not None , (
1146
+ "Data-frame processing failure, likely due to a internal bug. "
1147
+ "Please report this to "
1148
+ "https://github.com/plotly/plotly.py/issues/new and we will try to "
1149
+ "replicate and fix it."
1150
+ )
1148
1151
if field_name not in array_attrables :
1149
1152
args [field_name ] = str (col_name )
1150
1153
elif isinstance (args [field_name ], dict ):
@@ -1204,7 +1207,7 @@ def build_dataframe(args, constructor):
1204
1207
wide_mode = False
1205
1208
var_name = None # will likely be "variable" in wide_mode
1206
1209
wide_cross_name = None # will likely be "index" in wide_mode
1207
- value_name = "value"
1210
+ value_name = None # will likely be "value" in wide_mode
1208
1211
hist2d_types = [go .Histogram2d , go .Histogram2dContour ]
1209
1212
if constructor in cartesians :
1210
1213
if wide_x and wide_y :
@@ -1220,7 +1223,9 @@ def build_dataframe(args, constructor):
1220
1223
"at the moment."
1221
1224
)
1222
1225
args ["wide_variable" ] = list (df_input .columns )
1223
- var_name = df_input .columns .name or "variable"
1226
+ var_name = df_input .columns .name
1227
+ if var_name in [None , "value" , "index" ] or var_name in df_input :
1228
+ var_name = "variable"
1224
1229
if constructor == go .Funnel :
1225
1230
wide_orientation = args .get ("orientation" , None ) or "h"
1226
1231
else :
@@ -1240,6 +1245,10 @@ def build_dataframe(args, constructor):
1240
1245
if not no_x and not no_y :
1241
1246
wide_cross_name = "__x__" if wide_y else "__y__"
1242
1247
1248
+ if wide_mode :
1249
+ value_name = _escape_col_name (df_input , "value" , [])
1250
+ var_name = _escape_col_name (df_input , var_name , [])
1251
+
1243
1252
missing_bar_dim = None
1244
1253
if constructor in [go .Scatter , go .Bar , go .Funnel ] + hist2d_types :
1245
1254
if not wide_mode and (no_x != no_y ):
@@ -1262,14 +1271,10 @@ def build_dataframe(args, constructor):
1262
1271
"at the moment."
1263
1272
)
1264
1273
args ["wide_cross" ] = df_input .index
1265
- wide_cross_name = df_input .index .name or "index"
1266
1274
else :
1267
- wide_cross_name = _escape_col_name (df_input , "index" )
1268
- args ["wide_cross" ] = Range (label = wide_cross_name )
1269
-
1270
- if wide_mode :
1271
- var_name = _escape_col_name (df_input , var_name )
1272
- value_name = _escape_col_name (df_input , value_name )
1275
+ args ["wide_cross" ] = Range (
1276
+ label = _escape_col_name (df_input , "index" , [var_name , value_name ])
1277
+ )
1273
1278
1274
1279
# now that things have been prepped, we do the systematic rewriting of `args`
1275
1280
@@ -1281,7 +1286,7 @@ def build_dataframe(args, constructor):
1281
1286
# the special-case and wide-mode handling by further rewriting args and/or mutating
1282
1287
# df_output
1283
1288
1284
- count_name = _escape_col_name (df_output , "count" )
1289
+ count_name = _escape_col_name (df_output , "count" , [ var_name , value_name ] )
1285
1290
if not wide_mode and missing_bar_dim and constructor == go .Bar :
1286
1291
# now that we've populated df_output, we check to see if the non-missing
1287
1292
# dimension is categorical: if so, then setting the missing dimension to a
@@ -1306,19 +1311,27 @@ def build_dataframe(args, constructor):
1306
1311
# columns, keeping track of various names and manglings set up above
1307
1312
wide_value_vars = [c for c in args ["wide_variable" ] if c not in wide_id_vars ]
1308
1313
del args ["wide_variable" ]
1314
+ if wide_cross_name == "__x__" :
1315
+ wide_cross_name = args ["x" ]
1316
+ elif wide_cross_name == "__y__" :
1317
+ wide_cross_name = args ["y" ]
1318
+ else :
1319
+ wide_cross_name = args ["wide_cross" ]
1309
1320
del args ["wide_cross" ]
1310
1321
df_output = df_output .melt (
1311
1322
id_vars = wide_id_vars ,
1312
1323
value_vars = wide_value_vars ,
1313
1324
var_name = var_name ,
1314
1325
value_name = value_name ,
1315
1326
)
1327
+ assert len (df_output .columns ) == len (set (df_output .columns )), (
1328
+ "Wide-mode name-inference failure, likely due to a internal bug. "
1329
+ "Please report this to "
1330
+ "https://github.com/plotly/plotly.py/issues/new and we will try to "
1331
+ "replicate and fix it."
1332
+ )
1316
1333
df_output [var_name ] = df_output [var_name ].astype (str )
1317
1334
orient_v = wide_orientation == "v"
1318
- if wide_cross_name == "__x__" :
1319
- wide_cross_name = args ["x" ]
1320
- if wide_cross_name == "__y__" :
1321
- wide_cross_name = args ["y" ]
1322
1335
1323
1336
if constructor in [go .Scatter , go .Funnel ] + hist2d_types :
1324
1337
args ["x" if orient_v else "y" ] = wide_cross_name
0 commit comments