| 138 | self.band_labels.text = ["{:.0%}".format(b) for b in band_data] |
| 139 | |
| 140 | def update_data(self, *args): |
| 141 | self.update_bands(None) |
| 142 | rows = list(self.data.index) |
| 143 | n = len(rows) |
| 144 | |
| 145 | # spokes representing each data set |
| 146 | self.spoke_data_t = np.linspace(0, 2 * np.pi, n + 1)[:-1] |
| 147 | spoke_data_x, spoke_data_y = ( |
| 148 | np.sin(self.spoke_data_t), |
| 149 | np.cos(self.spoke_data_t), |
| 150 | ) |
| 151 | |
| 152 | # Update mark data based on data changes |
| 153 | with self.spokes.hold_sync(): |
| 154 | self.spokes.x = np.column_stack( |
| 155 | [self.scaled_band_data[1] * spoke_data_x, spoke_data_x] |
| 156 | ) |
| 157 | self.spokes.y = np.column_stack( |
| 158 | [self.scaled_band_data[1] * spoke_data_y, spoke_data_y] |
| 159 | ) |
| 160 | |
| 161 | scaled_data = (self.data.values - self.data_range[0]) / ( |
| 162 | self.data_range[1] - self.data_range[0] |
| 163 | ) |
| 164 | data_x = scaled_data * np.sin(self.spoke_data_t)[:, np.newaxis] |
| 165 | data_y = scaled_data * np.cos(self.spoke_data_t)[:, np.newaxis] |
| 166 | |
| 167 | # update data lines |
| 168 | with self.loops.hold_sync(): |
| 169 | self.loops.x = np.column_stack([data_x.T, data_x.T[:, 0]]) |
| 170 | self.loops.y = np.column_stack([data_y.T, data_y.T[:, 0]]) |
| 171 | if self.fill: |
| 172 | self.loops.fill = "inside" |
| 173 | self.loops.fill_opacities = [0.2] * len(self.loops.y) |
| 174 | else: |
| 175 | self.loops.fill = "none" |
| 176 | self.loops.fill_opacities = [0.0] * len(self.loops.y) |
| 177 | self.loops.labels = [str(c) for c in self.data.columns] |
| 178 | |
| 179 | # update spoke labels |
| 180 | t = np.linspace(0, 2 * np.pi, n + 1) |
| 181 | with self.spoke_labels.hold_sync(): |
| 182 | self.spoke_labels.text = [str(row) for row in rows] |
| 183 | self.spoke_labels.x = np.sin(t) |
| 184 | self.spoke_labels.y = np.cos(t) |
| 185 | |
| 186 | def update_fill(self, *args): |
| 187 | if self.fill: |