function transformOne(trace, state) {
    var i, j, k, attr, srcArray, groupName, newTrace, transforms, arrayLookup;
    var groupNameObj;

    var opts = state.transform;
    var transformIndex = state.transformIndex;
    var groups = trace.transforms[transformIndex].groups;
    var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts);

    if(!(Lib.isArrayOrTypedArray(groups)) || groups.length === 0) {
        return [trace];
    }

    var groupNames = Lib.filterUnique(groups);
    var newData = new Array(groupNames.length);
    var len = groups.length;

    var arrayAttrs = PlotSchema.findArrayAttributes(trace);

    var styles = opts.styles || [];
    var styleLookup = {};
    for(i = 0; i < styles.length; i++) {
        styleLookup[styles[i].target] = styles[i].value;
    }

    if(opts.styles) {
        groupNameObj = Lib.keyedContainer(opts, 'styles', 'target', 'value.name');
    }

    // An index to map group name --> expanded trace index
    var indexLookup = {};
    var indexCnts = {};

    for(i = 0; i < groupNames.length; i++) {
        groupName = groupNames[i];
        indexLookup[groupName] = i;
        indexCnts[groupName] = 0;

        // Start with a deep extend that just copies array references.
        newTrace = newData[i] = Lib.extendDeepNoArrays({}, trace);
        newTrace._group = groupName;
        newTrace.transforms[transformIndex]._indexToPoints = {};

        var suppliedName = null;
        if(groupNameObj) {
            suppliedName = groupNameObj.get(groupName);
        }

        if(suppliedName || suppliedName === '') {
            newTrace.name = suppliedName;
        } else {
            newTrace.name = Lib.templateString(opts.nameformat, {
                trace: trace.name,
                group: groupName
            });
        }

        // In order for groups to apply correctly to other transform data (e.g.
        // a filter transform), we have to break the connection and clone the
        // transforms so that each group writes grouped values into a different
        // destination. This function does not break the array reference
        // connection between the split transforms it creates. That's handled in
        // initialize, which creates a new empty array for each arrayAttr.
        transforms = newTrace.transforms;
        newTrace.transforms = [];
        for(j = 0; j < transforms.length; j++) {
            newTrace.transforms[j] = Lib.extendDeepNoArrays({}, transforms[j]);
        }

        // Initialize empty arrays for the arrayAttrs, to be split in the next step
        for(j = 0; j < arrayAttrs.length; j++) {
            Lib.nestedProperty(newTrace, arrayAttrs[j]).set([]);
        }
    }

    // For each array attribute including those nested inside this and other
    // transforms (small note that we technically only need to do this for
    // transforms that have not yet been applied):
    for(k = 0; k < arrayAttrs.length; k++) {
        attr = arrayAttrs[k];

        // Cache all the arrays to which we'll push:
        for(j = 0, arrayLookup = []; j < groupNames.length; j++) {
            arrayLookup[j] = Lib.nestedProperty(newData[j], attr).get();
        }

        // Get the input data:
        srcArray = Lib.nestedProperty(trace, attr).get();

        // Send each data point to the appropriate expanded trace:
        for(j = 0; j < len; j++) {
            // Map group data --> trace index --> array and push data onto it
            arrayLookup[indexLookup[groups[j]]].push(srcArray[j]);
        }
    }

    for(j = 0; j < len; j++) {
        newTrace = newData[indexLookup[groups[j]]];

        var indexToPoints = newTrace.transforms[transformIndex]._indexToPoints;
        indexToPoints[indexCnts[groups[j]]] = originalPointsAccessor(j);
        indexCnts[groups[j]]++;
    }

    for(i = 0; i < groupNames.length; i++) {
        groupName = groupNames[i];
        newTrace = newData[i];

        Plots.clearExpandedTraceDefaultColors(newTrace);

        // there's no need to coerce styleLookup[groupName] here
        // as another round of supplyDefaults is done on the transformed traces
        newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {});
    }

    return newData;
}
Apply transform !!!

@param {array} data
 array of transformed traces (is [fullTrace] upon first transform)

@param {object} state
 state object which includes:
     - transform {object} full transform attributes
     - fullTrace {object} full trace object which is being transformed
     - fullData {array} full pre-transform(s) data array
     - layout {object} the plot's (not-so-full) layout

@return {object} newData
 array of transformed traces