使用浮动滑块手动调整标记位置

问题描述 投票:0回答:1

嘿嘿,

我目前正在尝试通过 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")
python plotly ipywidgets
1个回答
0
投票

我没有看到

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")
© www.soinside.com 2019 - 2024. All rights reserved.