mapdata 2.12.0__tar.gz → 2.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.12.0
3
+ Version: 2.13.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -24,8 +24,8 @@
24
24
  #
25
25
  # ==================================================================
26
26
 
27
- version = "2.12.0"
28
- vdate = "2023-12-28"
27
+ version = "2.13.0"
28
+ vdate = "2023-12-30"
29
29
 
30
30
  copyright = "2023"
31
31
 
@@ -460,14 +460,21 @@ class CsvFile(object):
460
460
 
461
461
 
462
462
  def sort_columns(columns):
463
- # Sorts a list of one or two sublists, where each sublist is a column. Rows are sorted by the first column.
463
+ # Sorts a list of one, two, or three sublists, where each sublist is a column. Rows are sorted by the first column.
464
464
  # The returned value is also column-wise, but sorted by rows.
465
465
  if len(columns) == 1:
466
466
  return sorted(columns)
467
467
  nrows = len(columns[0])
468
- rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
469
- rowdata.sort()
470
- return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
468
+ if len(columns) == 2:
469
+ rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
470
+ rowdata.sort()
471
+ return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
472
+ else:
473
+ # len(columns) should be 3, though this is not checked
474
+ rowdata = [[columns[0][i], columns[1][i], columns[2][i]] for i in range(nrows)]
475
+ rowdata.sort()
476
+ return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)], [rowdata[i][2] for i in range(nrows)]]
477
+
471
478
 
472
479
  def treeview_sort_column(tv, col, reverse):
473
480
  # Sort columns in Tkinter Treeview. From https://stackoverflow.com/questions/1966929/tk-treeview-column-sort#1967793
@@ -1445,8 +1452,10 @@ class MapUI(object):
1445
1452
  clone.ylog_var.set(plot_obj.ylog_var.get())
1446
1453
  clone.x_sel["values"] = copy.copy(plot_obj.x_sel["values"])
1447
1454
  clone.y_sel["values"] = copy.copy(plot_obj.y_sel["values"])
1455
+ clone.groupby_sel["values"] = copy.copy(plot_obj.groupby_sel["values"])
1448
1456
  clone.x_sel["state"] = plot_obj.x_sel["state"]
1449
1457
  clone.y_sel["state"] = plot_obj.y_sel["state"]
1458
+ clone.groupby_sel["state"] = plot_obj.groupby_sel["state"]
1450
1459
  clone.xlog_ck["state"] = plot_obj.xlog_ck["state"]
1451
1460
  clone.ylog_ck["state"] = plot_obj.ylog_ck["state"]
1452
1461
  clone.data_btn["state"] = plot_obj.data_btn["state"]
@@ -2788,7 +2797,7 @@ class PlotDialog(object):
2788
2797
  self.plot_title = None
2789
2798
  # For histogram
2790
2799
  self.bins = 10
2791
- # For groups at Jenks breaks on Q-Q plots
2800
+ # For display of groups at Jenks breaks on Q-Q plots
2792
2801
  self.qq_groups = False
2793
2802
 
2794
2803
  def set_autoupdate():
@@ -2818,6 +2827,7 @@ class PlotDialog(object):
2818
2827
  self.type_sel = ttk.Combobox(ctrl_frame, state="readonly", textvariable=self.type_var, width=20, height=15,
2819
2828
  values=["Box plot", "Breaks groups", "Breaks optimum", "Category counts", "Categorical KD plot", "Categorical stripchart", "Empirical CDF", "Histogram", "Kernel density (KD) plot", "Line plot", "Min-max plot", "Normal Q-Q plot", "Scatter plot", "Violin plot", "Y range plot"])
2820
2829
  self.type_sel.grid(row=0, column=1, columnspan=2, sticky=tk.W, padx=(3,6), pady=(3,3))
2830
+ self.type_sel["state"] = "normal"
2821
2831
  self.type_sel.bind("<<ComboboxSelected>>", self.set_xy)
2822
2832
 
2823
2833
  self.sel_only_var = tk.StringVar(ctrl_frame, "0")
@@ -2833,14 +2843,14 @@ class PlotDialog(object):
2833
2843
  self.x_var = tk.StringVar(ctrl_frame, "")
2834
2844
  x_lbl = ttk.Label(ctrl_frame, text="X column:")
2835
2845
  x_lbl.grid(row=0, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2836
- self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=20)
2846
+ self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=24)
2837
2847
  self.x_sel.grid(row=0, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2838
2848
  self.x_sel.bind("<<ComboboxSelected>>", self.x_changed)
2839
2849
 
2840
2850
  self.y_var = tk.StringVar(ctrl_frame, "")
2841
2851
  y_lbl = ttk.Label(ctrl_frame, text="Y column:")
2842
2852
  y_lbl.grid(row=1, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2843
- self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=20)
2853
+ self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=24)
2844
2854
  self.y_sel.grid(row=1, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2845
2855
  self.y_sel.bind("<<ComboboxSelected>>", self.y_changed)
2846
2856
 
@@ -2854,6 +2864,13 @@ class PlotDialog(object):
2854
2864
  onvalue="1", offvalue="0")
2855
2865
  self.ylog_ck.grid(row=1, column=5, sticky=tk.W, padx=(6,6), pady=(3,3))
2856
2866
 
2867
+ self.groupby_var = tk.StringVar(ctrl_frame, "")
2868
+ groupby_lbl = ttk.Label(ctrl_frame, text="Group by:")
2869
+ groupby_lbl.grid(row=2, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2870
+ self.groupby_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.groupby_var, width=24)
2871
+ self.groupby_sel.grid(row=2, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2872
+ self.groupby_sel.bind("<<ComboboxSelected>>", self.groupby_changed)
2873
+
2857
2874
  # Plot
2858
2875
  self.content_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
2859
2876
  self.content_frame.grid(row=2, column=0, sticky=tk.NSEW)
@@ -2956,8 +2973,12 @@ class PlotDialog(object):
2956
2973
  self.ylog_ck["state"] = "normal"
2957
2974
  self.q_redraw(args)
2958
2975
 
2976
+ def groupby_changed(self, *args):
2977
+ self.q_redraw(args)
2978
+
2959
2979
  def set_xy(self, *args):
2960
2980
  # Enable X and Y value selection, and set Combobox values based on plot type and column types.
2981
+ # Also sets the groupby Combobox values if applicable.
2961
2982
  self.plotfig.clear()
2962
2983
  self.plot_title = None
2963
2984
  self.dlg.bind("<Alt-b>")
@@ -2984,17 +3005,21 @@ class PlotDialog(object):
2984
3005
  date_columns.sort()
2985
3006
  self.x_var.set('')
2986
3007
  self.y_var.set('')
3008
+ self.groupby_var.set('')
2987
3009
  self.xlog_ck["state"] = "normal"
2988
3010
  self.ylog_ck["state"] = "normal"
2989
3011
  self.x_sel["state"] = "readonly"
2990
3012
  self.y_sel["state"] = "readonly"
3013
+ self.groupby_sel["state"] = "readonly"
2991
3014
  plot_type = self.type_var.get()
2992
3015
  if plot_type == "Category counts":
3016
+ self.x_sel["state"] = "normal"
2993
3017
  self.x_sel["values"] = categ_columns
2994
3018
  self.xlog_ck["state"] = "disabled"
2995
3019
  self.y_sel["state"] = "disabled"
2996
3020
  self.ylog_ck["state"] = "disabled"
2997
3021
  elif plot_type in ("Histogram", "Empirical CDF", "Kernel density (KD) plot", "Normal Q-Q plot", "Breaks groups", "Breaks optimum"):
3022
+ self.x_sel["state"] = "normal"
2998
3023
  self.x_sel["values"] = numeric_columns
2999
3024
  self.y_sel["state"] = "disabled"
3000
3025
  self.ylog_ck["state"] = "disabled"
@@ -3005,16 +3030,24 @@ class PlotDialog(object):
3005
3030
  elif plot_type in ("Box plot", "Categorical KD plot", "Categorical stripchart", "Violin plot"):
3006
3031
  xcols = list(set(categ_columns) | set(date_columns))
3007
3032
  xcols.sort()
3033
+ self.x_sel["state"] = "normal"
3008
3034
  self.x_sel["values"] = xcols
3009
3035
  self.xlog_ck["state"] = "disabled"
3010
3036
  self.y_sel["values"] = numeric_columns
3011
3037
  elif plot_type == "Min-max plot":
3038
+ self.x_sel["state"] = "normal"
3012
3039
  self.x_sel["values"] = quant_columns
3040
+ self.y_sel["state"] = "normal"
3013
3041
  self.y_sel["values"] = categ_columns2
3014
3042
  self.ylog_ck["state"] = "disabled"
3015
3043
  else:
3044
+ self.x_sel["state"] = "normal"
3016
3045
  self.x_sel["values"] = quant_columns
3046
+ self.y_sel["state"] = "normal"
3017
3047
  self.y_sel["values"] = quant_columns
3048
+ if plot_type in ("Scatter plot", "Line plot"):
3049
+ self.groupby_sel["state"] = "normal"
3050
+ self.groupby_sel["values"] = [''] + categ_columns2
3018
3051
 
3019
3052
  def q_redraw(self, get_data=True, *args):
3020
3053
  # Conditionally (re)draw the plot.
@@ -3041,6 +3074,8 @@ class PlotDialog(object):
3041
3074
  column_list = [self.x_var.get()]
3042
3075
  if self.y_var.get() != '':
3043
3076
  column_list.append(self.y_var.get())
3077
+ if self.groupby_var.get() != '':
3078
+ column_list.append(self.groupby_var.get())
3044
3079
  # Get either only the selected data or all data.
3045
3080
  if self.sel_only_var.get() == "1":
3046
3081
  dataset = self.parent.get_sel_data(column_list)
@@ -3082,6 +3117,8 @@ class PlotDialog(object):
3082
3117
  # Set data labels
3083
3118
  if self.y_var.get() != '':
3084
3119
  self.data_labels = [self.x_var.get(), self.y_var.get()]
3120
+ if self.groupby_var.get() != '':
3121
+ self.data_labels.append(self.groupby_var.get())
3085
3122
  else:
3086
3123
  self.data_labels = [self.x_var.get()]
3087
3124
  # Log-transform data if specified.
@@ -3158,7 +3195,7 @@ class PlotDialog(object):
3158
3195
  self.plot_data_labels = [self.data_labels[0], "Cumulative frequency"]
3159
3196
  elif plot_type == "Kernel density (KD) plot":
3160
3197
  self.plot_data = self.dataset
3161
- #self.plot_data_labels = [self.x_var.get()]
3198
+ self.plot_data_labels = [self.x_var.get()]
3162
3199
  elif plot_type == "Min-max plot":
3163
3200
  # Min and max X for each Y
3164
3201
  y_vals = list(set(self.dataset[1]))
@@ -3209,10 +3246,16 @@ class PlotDialog(object):
3209
3246
  self.plot_data_labels = [self.data_labels[0], self.data_labels[1] + " min", self.data_labels[1] + " max"]
3210
3247
  elif plot_type == "Line plot":
3211
3248
  # Sort by X
3212
- ds = list(zip(self.dataset[0], self.dataset[1]))
3249
+ if self.groupby_var.get() == '':
3250
+ ds = list(zip(self.dataset[0], self.dataset[1]))
3251
+ else:
3252
+ ds = list(zip(self.dataset[0], self.dataset[1], self.dataset[2]))
3213
3253
  ds.sort()
3214
3254
  ds2 = list(zip(*ds))
3215
- self.plot_data = [list(ds2[0]), list(ds2[1])]
3255
+ if self.groupby_var.get() == '':
3256
+ self.plot_data = [list(ds2[0]), list(ds2[1])]
3257
+ else:
3258
+ self.plot_data = [list(ds2[0]), list(ds2[1]), list(ds2[2])]
3216
3259
  self.plot_data_labels = self.data_labels
3217
3260
  elif plot_type in ("Histogram", "Scatter plot", "Y range plot"):
3218
3261
  # No special preparation
@@ -3232,11 +3275,31 @@ class PlotDialog(object):
3232
3275
  self.plot_axes.set_xlabel(self.x_var.get())
3233
3276
  self.plot_axes.set_ylabel("Counts")
3234
3277
  elif plot_type == "Scatter plot":
3235
- self.plot_axes.scatter(self.plot_data[0], self.plot_data[1])
3278
+ if self.groupby_var.get() == '':
3279
+ self.plot_axes.scatter(self.plot_data[0], self.plot_data[1])
3280
+ else:
3281
+ groups = list(set(self.plot_data[2]))
3282
+ groups.sort()
3283
+ datarows = len(self.plot_data[0])
3284
+ for g in groups:
3285
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3286
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3287
+ self.plot_axes.plot(pdx, pdy, marker='o', linestyle='', label=g)
3288
+ self.plot_axes.legend()
3236
3289
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3237
3290
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3238
3291
  elif plot_type == "Line plot":
3239
- self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3292
+ if self.groupby_var.get() == '':
3293
+ self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3294
+ else:
3295
+ groups = list(set(self.plot_data[2]))
3296
+ groups.sort()
3297
+ datarows = len(self.plot_data[0])
3298
+ for g in groups:
3299
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3300
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3301
+ self.plot_axes.plot(pdx, pdy, label=g)
3302
+ self.plot_axes.legend()
3240
3303
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3241
3304
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3242
3305
  elif plot_type == "Breaks groups":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.12.0
3
+ Version: 2.13.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -5,7 +5,7 @@ with io.open('README.md', encoding='utf-8') as f:
5
5
  long_description = f.read()
6
6
 
7
7
  setuptools.setup(name='mapdata',
8
- version='2.12.0',
8
+ version='2.13.0',
9
9
  description="An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database",
10
10
  author='Dreas Nielsen',
11
11
  author_email='dreas.nielsen@gmail.com',
File without changes
File without changes
File without changes
File without changes
File without changes