嘿嘿,
我目前正在尝试通过 ipywidgets 根据用户输入为数据分配标记。第一个用户输入步骤,定义偏移量、步长、静止长度等到目前为止都是有效的。但是,我想添加选项来手动调整每个阶段的开始和结束标记位置。我弄清楚了如何获取每个开始结束标记的索引,但是每当我按运行交互时,标记位置不会改变并且标记会消失。
我在这里遗漏了什么或者为什么会发生这种情况?
from ipywidgets import Output, VBox, HBox, FloatSlider, Button, interact
import plotly.graph_objs as go
import pandas as pd
output = Output()
markers = {} # Initialize markers dictionary outside of set_markers function
np.random.seed(0)
df = pd.DataFrame({
'timeinsec': np.linspace(0, 1000, 100), # Time from 0 to 1000 seconds
"data": np.random.rand(100) # Random values
})
def set_markers(df, x_col, y_col, offset, step_length, rest_length, num_intervals, last_step_length, start_velocity, velocity_increment):
with output:
output.clear_output(wait=True)
fig = go.Figure()
fig.add_trace(go.Scatter(x=df[x_col], y=df[y_col], mode='markers', name='original', marker=dict(color='red', size=8, line=dict(width=1, color='DarkSlateGrey'))))
velocities = [start_velocity + i * velocity_increment for i in range(num_intervals)]
for i in range(num_intervals):
start = offset + i * (step_length + rest_length)
end = start + (last_step_length if i == num_intervals - 1 else step_length)
fig.add_shape(type="line", x0=start, y0=df[y_col].min(), x1=start, y1=df[y_col].max(), line=dict(color="blue", width=2))
fig.add_shape(type="line", x0=end, y0=df[y_col].min(), x1=end, y1=df[y_col].max(), line=dict(color="blue", width=2,))
markers[f"Start{i+1}"] = start # Create start marker positions
markers[f"End{i+1}"] = end # Create end marker positions
df.loc[(df[x_col] >= start) & (df[x_col] <= end), 'v'] = velocities[i]
# Create a new marker for the end position of each stage
fig.add_shape(type="line", x0=end, y0=df[y_col].min(), x1=end, y1=df[y_col].max(), line=dict(color="green", width=2))
# Calculate velocity for the last stage based on proportional length
if num_intervals > 1:
stage_per = (last_step_length * 100 / step_length) / 100
new_v = velocities[-2] + velocity_increment * stage_per
df.loc[(df[x_col] >= start) & (df[x_col] <= end), 'v'] = new_v
df.v.fillna(0,inplace = True)
fig.update_layout(title='Assign Stage:', xaxis_title=x_col, yaxis_title=y_col)
fig.show()
# Add sliders to adjust the position of the start and end markers for each stage
sliders_column_1 = VBox()
sliders_column_2 = VBox()
for i in range(num_intervals):
start_marker_pos = markers.get(f"Start{i+1}", 0) # Get initial position from markers dictionary
end_marker_pos = markers.get(f"End{i+1}", 0) # Get initial position from markers dictionary
start_marker_slider = FloatSlider(value=start_marker_pos, min=0, max=df[x_col].max(), description=f'Start {i+1}:')
sliders_column_1.children += (start_marker_slider,) # Add slider to the VBox
end_marker_slider = FloatSlider(value=end_marker_pos, min=0, max=df[x_col].max(), description=f'End {i+1}:')
sliders_column_2.children += (end_marker_slider,) # Add slider to the VBox
# Arrange sliders in two columns
sliders_box = HBox([sliders_column_1, sliders_column_2])
display(sliders_box)
def update_marker_positions(**kwargs):
with output:
output.clear_output(wait=True)
shapes = []
for i in range(num_intervals):
if f"Start{i+1}" in kwargs and f"End{i+1}" in kwargs:
start_marker_pos = kwargs[f"Start{i+1}"]
end_marker_pos = kwargs[f"End{i+1}"]
markers[f"Start{i+1}"] = start_marker_pos
markers[f"End{i+1}"] = end_marker_pos
shapes.append(dict(type="line", x0=start_marker_pos, y0=df[y_col].min(), x1=start_marker_pos, y1=df[y_col].max(), line=dict(color="blue", width=2)))
shapes.append(dict(type="line", x0=end_marker_pos, y0=df[y_col].min(), x1=end_marker_pos, y1=df[y_col].max(), line=dict(color="green", width=2)))
fig.update_layout(shapes=shapes)
fig.show()
interact_manual(update_marker_positions)
def interactive_line_plot(df, x_col, y_col):
offset = IntSlider(value=10, min=0, max=df[x_col].max(), step=1, description='Offset:')
step_length = IntSlider(value=240, min=1, max=500, step=1, description='Step Length:')
rest_length = IntSlider(value=30, min=1, max=500, step=1, description='Rest Length:')
num_intervals = IntSlider(value=5, min=1, max=10, step=1, description='Intervals:')
last_step_length = IntSlider(value=240, min=1, max=500, step=1, description=' t Last Step:')
start_velocity = FloatText(value=8.0, description='Start:')
velocity_increment = FloatText(value=1.0, description='Increment:')
update_plot_button = Button(description="Update Plot")
update_plot_button.on_click(lambda b: set_markers(df, x_col, y_col, offset.value, step_length.value, rest_length.value, num_intervals.value, last_step_length.value, start_velocity.value, velocity_increment.value))
control_box = VBox([offset, step_length, rest_length, num_intervals, last_step_length, start_velocity, velocity_increment, update_plot_button])
display(HBox([control_box, output]))
interactive_line_plot(df, "timeinsec", "data")
我没有看到
kwargs
传递到 update_marker_positions()
传递任何有用的东西。 (也许你有不同的运气?)
因此,每次您检查 if f"Start{i+1}" in kwargs and f"End{i+1}" in kwargs:
时,它都会计算为 False
并且什么也没有发生。
这也意味着您无法使用 start_marker_pos = kwargs[f"Start{i+1}"]
和 end_marker_pos = kwargs[f"End{i+1}"
来获取滑块的更新值。
实际上,我不确定在更新作业之前,
if f"Start{i+1}" in kwargs and f"End{i+1}" in kwargs:
行是否需要任何条件;但是,我保留了它以更接近您的原始代码。
下面的内容目前似乎在我手中起作用,让您有机会完善每个间隔的开始和结束。我基本上更新了如何处理
start_marker_pos = kwargs[f"Start{i+1}"]
和 end_marker_pos = kwargs[f"End{i+1}"
:
from ipywidgets import Output, VBox, HBox, FloatSlider, Button, interact, IntSlider, FloatText, interact_manual
import numpy as np
import plotly.graph_objs as go
import pandas as pd
output = Output()
markers = {} # Initialize markers dictionary outside of set_markers function
np.random.seed(0)
df = pd.DataFrame({
'timeinsec': np.linspace(0, 1000, 100), # Time from 0 to 1000 seconds
"data": np.random.rand(100) # Random values
})
def set_markers(df, x_col, y_col, offset, step_length, rest_length, num_intervals, last_step_length, start_velocity, velocity_increment):
with output:
output.clear_output(wait=True)
fig = go.Figure()
fig.add_trace(go.Scatter(x=df[x_col], y=df[y_col], mode='markers', name='original', marker=dict(color='red', size=8, line=dict(width=1, color='DarkSlateGrey'))))
velocities = [start_velocity + i * velocity_increment for i in range(num_intervals)]
for i in range(num_intervals):
start = offset + i * (step_length + rest_length)
end = start + (last_step_length if i == num_intervals - 1 else step_length)
fig.add_shape(type="line", x0=start, y0=df[y_col].min(), x1=start, y1=df[y_col].max(), line=dict(color="blue", width=2))
fig.add_shape(type="line", x0=end, y0=df[y_col].min(), x1=end, y1=df[y_col].max(), line=dict(color="blue", width=2,))
markers[f"Start{i+1}"] = start # Create start marker positions
markers[f"End{i+1}"] = end # Create end marker positions
df.loc[(df[x_col] >= start) & (df[x_col] <= end), 'v'] = velocities[i]
# Create a new marker for the end position of each stage
fig.add_shape(type="line", x0=end, y0=df[y_col].min(), x1=end, y1=df[y_col].max(), line=dict(color="green", width=2))
# Calculate velocity for the last stage based on proportional length
if num_intervals > 1:
stage_per = (last_step_length * 100 / step_length) / 100
new_v = velocities[-2] + velocity_increment * stage_per
df.loc[(df[x_col] >= start) & (df[x_col] <= end), 'v'] = new_v
df.v.fillna(0,inplace = True)
fig.update_layout(title='Assign Stage:', xaxis_title=x_col, yaxis_title=y_col)
fig.show()
# Add sliders to adjust the position of the start and end markers for each stage
sliders_column_1 = VBox()
sliders_column_2 = VBox()
for i in range(num_intervals):
start_marker_pos = markers.get(f"Start{i+1}", 0) # Get initial position from markers dictionary
end_marker_pos = markers.get(f"End{i+1}", 0) # Get initial position from markers dictionary
start_marker_slider = FloatSlider(value=start_marker_pos, min=0, max=df[x_col].max(), description=f'Start {i+1}:')
sliders_column_1.children += (start_marker_slider,) # Add slider to the VBox
end_marker_slider = FloatSlider(value=end_marker_pos, min=0, max=df[x_col].max(), description=f'End {i+1}:')
sliders_column_2.children += (end_marker_slider,) # Add slider to the VBox
# Arrange sliders in two columns
sliders_box = HBox([sliders_column_1, sliders_column_2])
display(sliders_box)
def update_marker_positions(**kwargs):
with output:
output.clear_output(wait=True)
shapes = []
for i in range(num_intervals):
if f"Start{i+1}" in markers and f"End{i+1}" in markers:
start_marker_pos = list(sliders_column_1.children)[i].value
end_marker_pos = list(sliders_column_2.children)[i].value
markers[f"Start{i+1}"] = start_marker_pos
markers[f"End{i+1}"] = end_marker_pos
shapes.append(dict(type="line", x0=start_marker_pos, y0=df[y_col].min(), x1=start_marker_pos, y1=df[y_col].max(), line=dict(color="blue", width=2)))
shapes.append(dict(type="line", x0=end_marker_pos, y0=df[y_col].min(), x1=end_marker_pos, y1=df[y_col].max(), line=dict(color="green", width=2)))
fig.update_layout(shapes=shapes)
fig.show()
interact_manual(update_marker_positions)
def interactive_line_plot(df, x_col, y_col):
offset = IntSlider(value=10, min=0, max=df[x_col].max(), step=1, description='Offset:')
step_length = IntSlider(value=240, min=1, max=500, step=1, description='Step Length:')
rest_length = IntSlider(value=30, min=1, max=500, step=1, description='Rest Length:')
num_intervals = IntSlider(value=5, min=1, max=10, step=1, description='Intervals:')
last_step_length = IntSlider(value=240, min=1, max=500, step=1, description=' t Last Step:')
start_velocity = FloatText(value=8.0, description='Start:')
velocity_increment = FloatText(value=1.0, description='Increment:')
update_plot_button = Button(description="Update Plot")
update_plot_button.on_click(lambda b: set_markers(df, x_col, y_col, offset.value, step_length.value, rest_length.value, num_intervals.value, last_step_length.value, start_velocity.value, velocity_increment.value))
control_box = VBox([offset, step_length, rest_length, num_intervals, last_step_length, start_velocity, velocity_increment, update_plot_button])
display(HBox([control_box, output]))
interactive_line_plot(df, "timeinsec", "data")