Search code examples
pythonnormalization

Scale/normalize 3D data points along one axis?


For a research project, I am asked to classify different types of spine curvatures using 3D landmarks of vertebrae.

3D Plots of 2 patients' spine landmarks

What I want the plots to look like more or less

Since I am aiming to focus only on curvature, I need to scale/normalize the 3D lines along the Z-axis as patients differ in height and size. I am not sure how to approach this problem of z-axis scaling while maintaining the relationship of the x and y axes with regard to z.

print(spine_landmark_data_x.head(1).T)
print(spine_landmark_data_y.head(1).T)
print(spine_landmark_data_z.head(1).T)

Output of dataframes from above code (X,Y,Z coordinates in 3 separate dataframes)

test_x = spine_landmark_data_x.copy()
test_y = spine_landmark_data_y.copy()
test_z = spine_landmark_data_z.copy()

# Scale each patient z-axis from 0 to 1
for row in range(spine_landmark_data.shape[0]):
    test_z.iloc[row] = spine_landmark_data_z.iloc[row] - spine_landmark_data_z.iloc[row].min()
    test_z.iloc[row] = test_z.iloc[row] / test_z.iloc[row].max()
    
    test_y.iloc[row] = spine_landmark_data_y.iloc[row] - spine_landmark_data_y.iloc[row].min()
    test_y.iloc[row] = test_y.iloc[row] / test_y.iloc[row].max()
    
    test_x.iloc[row] = spine_landmark_data_x.iloc[row] - spine_landmark_data_x.iloc[row].min()
    test_x.iloc[row] = test_x.iloc[row] / test_x.iloc[row].max()

Plots that the above code produces


Solution

  • Would the following work?

    test_x = spine_landmark_data_x.copy()
    test_y = spine_landmark_data_y.copy()
    test_z = spine_landmark_data_z.copy()
    
    # Scale each patient z-axis from 0 to 1
    for row in range(spine_landmark_data.shape[0]):
        # scaling factor = 1.0 / (patient's z data range)
        scaling_factor = 1.0 / (spine_landmark_data_z.iloc[row].max() - spine_landmark_data_z.iloc[row].min())
        test_z.iloc[row] = (spine_landmark_data_z.iloc[row] - spine_landmark_data_z.iloc[row].min()) * scaling_factor
        test_y.iloc[row] = (spine_landmark_data_y.iloc[row] - spine_landmark_data_y.iloc[row].min()) * scaling_factor
        test_x.iloc[row] = (spine_landmark_data_x.iloc[row] - spine_landmark_data_x.iloc[row].min()) * scaling_factor
    

    It differs only slightly from your existing code in that it scales all axes by the same factor which was used to normalize the z range to [0, 1].