Correcting monotonicity errors using ArcGIS Desktop

Here is an ArcToolbox ready script for a tool that runs on an entire feature layer, or a selection if you happen to have features selected. The tool assumes the start point has a z lower than the end point for ascending monotonic vertices, or a start point with a higher z than the end point for descending monotonic vertices. Single vertices that do not fit the monotonic trend are given an average z value of the neighbouring vertices. If two or more consecutive non-monotonic vertices are found, the first are "flattened" the the z of the previous vertices. Flat line parts are skipped all together.

This is not pretty (thanks to arcpy geometry objects), and there's probably a better solution out there, but it works fairly well.

import arcpy
inFeat = arcpy.GetParameterAsText(0)   #Feature Layer
with arcpy.da.UpdateCursor(inFeat, ["OID@", "SHAPE@"]) as cursor:
    for row in cursor:
        geom = row[1]
        partNum = 0
        isIncreasing = None
        newGeom = arcpy.Array()
        for part in geom:
            newPart = arcpy.Array()
            # detemine increasing/decreasing
            if part[0].Z < part[len(part) - 1].Z:
                isIncreasing = True
            elif part[0].Z > part[len(part) - 1].Z:
                isIncreasing = False
            else:
                #flat line
                arcpy.AddMessage("Line {0} part {1} is flat. Skipping...".format(row[0],partNum))
                partNum += 1
                newGeom.add(part)
                continue
            zList = []
            for i in xrange(len(part)):
                pnt = part[i]
                zList.append(pnt.Z)
            for i in xrange(len(part)):
                pnt = part[i]
                z = zList[i]
                if (i != 0) and (i != len(part) - 1):
                    zN = zList[i+1]
                    zP = zList[i-1]
                    if isIncreasing:
                        if z < zP:
                            if zN > zP:
                                # interpolate Z (i.e. average)
                                zList[i] = (zP + zN) / 2
                                arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
                            else:
                                # Next Z is greater than Prev Z
                                # set Z equal to previous point
                                zList[i] = zP
                                arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
                    else:
                        if z > zP:
                            if zN < zP:
                                zList[i] = (zP + zN) / 2
                                arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
                            else:
                                zList[i] = zP
                                arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
                newPnt = arcpy.Point(part[i].X, part[i].Y, zList[i])
                newPart.add(newPnt)        
            newGeom.add(newPart)
        newShape = arcpy.Polyline(newGeom, None, True)
        row[1] = newShape             
        cursor.updateRow(row)