Decodes data to Numpy unicode string (numpy.unicode\_).

    Decodes `data` to Numpy unicode string (UTF-32), which is
    ``numpy.unicode_``, or an array of them. If it can't be decoded, it
    is returned as is. Unsigned integers, Python string types (``str``,
    ``bytes``), and ``numpy.bytes_`` are supported. If it is an array of
    ``numpy.bytes_``, an array of those all converted to
    ``numpy.unicode_`` is returned. Python 3.x ``bytes``, Python 2.x
    ``str``, and ``numpy.bytes_`` are assumed to be encoded in UTF-8.

    For an array of unsigned integers, it may be desirable to make an
    array with strings of some specified length as opposed to an array
    of the same size with each element being a one element string. This
    naturally arises when converting strings to unsigned integer types
    in the first place, so it needs to be reversible.  The `length`
    parameter specifies how many to group together into a string
    (desired string length). For 1d arrays, this is along its only
    dimension. For higher dimensional arrays, it is done along each row
    (across columns). So, for a 3x10x5 input array of uints and a
    `length` of 5, the output array would be a 3x2x5 of 5 element
    strings.

    Parameters
    ----------
    data : some type
        Data decode into a Numpy unicode string.
    length : int or None, optional
        The number of consecutive elements (in the case of unsigned
        integer `data`) to compose each string in the output array from.
        ``None`` indicates the full amount for a 1d array or the number
        of columns (full length of row) for a higher dimension array.

    Returns
    -------
    s : numpy.unicode\_ or numpy.ndarray of numpy.unicode\_ or data
        If `data` can be decoded into a ``numpy.unicode_`` or a
        ``numpy.ndarray`` of them, the decoded version is returned.
        Otherwise, `data` is returned unchanged.

    See Also
    --------
    convert_to_str
    convert_to_numpy_bytes
    numpy.unicode\_

def convert_to_numpy_str(data, length=None):
    """ Decodes data to Numpy unicode string (numpy.unicode\_).

    Decodes `data` to Numpy unicode string (UTF-32), which is
    ``numpy.unicode_``, or an array of them. If it can't be decoded, it
    is returned as is. Unsigned integers, Python string types (``str``,
    ``bytes``), and ``numpy.bytes_`` are supported. If it is an array of
    ``numpy.bytes_``, an array of those all converted to
    ``numpy.unicode_`` is returned. Python 3.x ``bytes``, Python 2.x
    ``str``, and ``numpy.bytes_`` are assumed to be encoded in UTF-8.

    For an array of unsigned integers, it may be desirable to make an
    array with strings of some specified length as opposed to an array
    of the same size with each element being a one element string. This
    naturally arises when converting strings to unsigned integer types
    in the first place, so it needs to be reversible.  The `length`
    parameter specifies how many to group together into a string
    (desired string length). For 1d arrays, this is along its only
    dimension. For higher dimensional arrays, it is done along each row
    (across columns). So, for a 3x10x5 input array of uints and a
    `length` of 5, the output array would be a 3x2x5 of 5 element
    strings.

    Parameters
    ----------
    data : some type
        Data decode into a Numpy unicode string.
    length : int or None, optional
        The number of consecutive elements (in the case of unsigned
        integer `data`) to compose each string in the output array from.
        ``None`` indicates the full amount for a 1d array or the number
        of columns (full length of row) for a higher dimension array.

    Returns
    -------
    s : numpy.unicode\_ or numpy.ndarray of numpy.unicode\_ or data
        If `data` can be decoded into a ``numpy.unicode_`` or a
        ``numpy.ndarray`` of them, the decoded version is returned.
        Otherwise, `data` is returned unchanged.

    See Also
    --------
    convert_to_str
    convert_to_numpy_bytes
    numpy.unicode\_

    """
    # The method of conversion depends on its type.
    if isinstance(data, np.unicode_) or (isinstance(data, np.ndarray) \
            and data.dtype.char == 'U'):
        # It is already an np.str_ or array of them, so nothing needs to
        # be done.
        return data
    elif (sys.hexversion >= 0x03000000 and isinstance(data, str)) \
           or (sys.hexversion < 0x03000000 \
           and isinstance(data, unicode)):
        # Easily converted through constructor.
        return np.unicode_(data)
    elif isinstance(data, (bytes, bytearray, np.bytes_)):
        # All of them can be decoded and then passed through the
        # constructor.
        return np.unicode_(data.decode('UTF-8'))
    elif isinstance(data, (np.uint8, np.uint16)):
        # They are single UTF-8 or UTF-16 scalars, which can be wrapped
        # into an array and recursed.
        return convert_to_numpy_str(np.atleast_1d(data))[0]
    elif isinstance(data, np.uint32):
        # It is just the uint32 version of the character, so it just
        # needs to be have the dtype essentially changed by having its
        # bytes read into ndarray.
        return np.ndarray(shape=tuple(), dtype='U1',
                          buffer=data.tostring())[()]
    elif isinstance(data, np.ndarray) and data.dtype.char == 'S':
        return np.char.encode(data, 'UTF-32')
    elif isinstance(data, np.ndarray) \
            and data.dtype.name in ('uint8', 'uint16', 'uint32'):
        # It is an ndarray of some uint type. How it is converted
        # depends on its shape. If its shape is just (), then it is just
        # a scalar wrapped in an array, which can be converted by
        # recursing the scalar value back into this function.
        shape = list(data.shape)
        if len(shape) == 0:
            return convert_to_numpy_str(data[()])

        # As there are more than one element, it gets a bit more
        # complicated. We need to take the subarrays of the specified
        # length along columns (1D arrays will be treated as row arrays
        # here), each of those converted to an str_ scalar (normal
        # string) and stuffed into a new array.
        #
        # If the length was not given, it needs to be set to full. Then
        # the shape of the new array needs to be calculated (divide the
        # appropriate dimension, which depends on the number of
        # dimentions).
        if len(shape) == 1:
            if length is None:
                length = shape[0]
            new_shape = (shape[0]//length,)
        else:
            if length is None:
                length = shape[-1]
            new_shape = copy.deepcopy(shape)
            new_shape[-1] //= length

        # numpy.char.decode will be used to decode. It needs the
        # encoding (UTF-8/16/32) which is gotten from the dtype. But it
        # also needs the data to be in big endian format, so it must be
        # byteswapped if it isn't. Without the swapping, an error occurs
        # since trailing nulls are dropped in numpy bytes_ arrays. The
        # dtype for each string element is just 'SX' where X is the
        # number of bytes.
        if data.dtype.name == 'uint8':
            encoding = 'UTF-8'
            swapbytes = False
            dt = 'S' + str(length)
        else:
            if data.dtype.name == 'uint16':
                encoding = 'UTF-16BE'
                dt = 'S' + str(2 * length)
            else:
                encoding = 'UTF-32BE'
                dt = 'S' + str(4 * length)
            if (data.dtype.byteorder == '<'
                or (sys.byteorder == 'little'
                    and data.dtype.byteorder == '=')):
                swapbytes = True
            else:
                swapbytes = False
        # Copy is needed to prevent errors.
        if swapbytes:
            return np.char.decode(data.copy().byteswap().view(dt),
                                  encoding)
        else:
            return np.char.decode(data.copy().view(dt), encoding)
    else:
        # Couldn't figure out what it is, so nothing can be done but
        # return it as is.
        return data