retrieve the channels listed in *channels* argument as *Signal*
        objects

        Parameters
        ----------
        channels : list
            list of items to be filtered; each item can be :

                * a channel name string
                * (channel name, group index, channel index) list or tuple
                * (channel name, group index) list or tuple
                * (None, group index, channel index) list or tuple
        dataframe : bool
            return pandas dataframe instead of list of Signals; default *False*
        record_offset : int
            record number offset; optimization to get the last part of signal samples
        raw : bool
            get raw channel samples; default *False*
        copy_master : bool
            option to get a new timestamps array for each selected Signal or to
            use a shared array for channels of the same channel group; default *False*

        dataframe: bool
            return a pandas DataFrame instead of a list of *Signals*; in this
            case the signals will be interpolated using the union of all
            timestamps

        Returns
        -------
        signals : list
            list of *Signal* objects based on the input channel list

        Examples
        --------
        >>> from asammdf import MDF, Signal
        >>> import numpy as np
        >>> t = np.arange(5)
        >>> s = np.ones(5)
        >>> mdf = MDF()
        >>> for i in range(4):
        ...     sigs = [Signal(s*(i*10+j), t, name='SIG') for j in range(1,4)]
        ...     mdf.append(sigs)
        ...
        >>> # select SIG group 0 default index 1 default, SIG group 3 index 1, SIG group 2 index 1 default and channel index 2 from group 1
        ...
        >>> mdf.select(['SIG', ('SIG', 3, 1), ['SIG', 2],  (None, 1, 2)])
        [<Signal SIG:
                samples=[ 1.  1.  1.  1.  1.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 31.  31.  31.  31.  31.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 21.  21.  21.  21.  21.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 12.  12.  12.  12.  12.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        ]

def select(self, channels, dataframe=False, record_offset=0, raw=False, copy_master=True):
        """ retrieve the channels listed in *channels* argument as *Signal*
        objects

        Parameters
        ----------
        channels : list
            list of items to be filtered; each item can be :

                * a channel name string
                * (channel name, group index, channel index) list or tuple
                * (channel name, group index) list or tuple
                * (None, group index, channel index) list or tuple
        dataframe : bool
            return pandas dataframe instead of list of Signals; default *False*
        record_offset : int
            record number offset; optimization to get the last part of signal samples
        raw : bool
            get raw channel samples; default *False*
        copy_master : bool
            option to get a new timestamps array for each selected Signal or to
            use a shared array for channels of the same channel group; default *False*

        dataframe: bool
            return a pandas DataFrame instead of a list of *Signals*; in this
            case the signals will be interpolated using the union of all
            timestamps

        Returns
        -------
        signals : list
            list of *Signal* objects based on the input channel list

        Examples
        --------
        >>> from asammdf import MDF, Signal
        >>> import numpy as np
        >>> t = np.arange(5)
        >>> s = np.ones(5)
        >>> mdf = MDF()
        >>> for i in range(4):
        ...     sigs = [Signal(s*(i*10+j), t, name='SIG') for j in range(1,4)]
        ...     mdf.append(sigs)
        ...
        >>> # select SIG group 0 default index 1 default, SIG group 3 index 1, SIG group 2 index 1 default and channel index 2 from group 1
        ...
        >>> mdf.select(['SIG', ('SIG', 3, 1), ['SIG', 2],  (None, 1, 2)])
        [<Signal SIG:
                samples=[ 1.  1.  1.  1.  1.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 31.  31.  31.  31.  31.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 21.  21.  21.  21.  21.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        , <Signal SIG:
                samples=[ 12.  12.  12.  12.  12.]
                timestamps=[0 1 2 3 4]
                unit=""
                info=None
                comment="">
        ]

        """

        # group channels by group index
        gps = {}

        indexes = []

        for item in channels:
            if isinstance(item, (list, tuple)):
                if len(item) not in (2, 3):
                    raise MdfException(
                        "The items used for filtering must be strings, "
                        "or they must match the first 3 argumens of the get "
                        "method"
                    )
                else:
                    group, index = self._validate_channel_selection(*item)
                    indexes.append((group, index))
                    if group not in gps:
                        gps[group] = {index}
                    else:
                        gps[group].add(index)
            else:
                name = item
                group, index = self._validate_channel_selection(name)
                indexes.append((group, index))
                if group not in gps:
                    gps[group] = {index}
                else:
                    gps[group].add(index)

        signals = {}

        for group in gps:
            grp = self.groups[group]
            data = self._load_data(grp, record_offset=record_offset)
            parents, dtypes = self._prepare_record(grp)

            channel_indexes = gps[group]

            signal_parts = defaultdict(list)
            master_parts = []

            sigs = {}

            for i, fragment in enumerate(data):
                if dtypes.itemsize:
                    grp.record = np.core.records.fromstring(fragment[0], dtype=dtypes)
                else:
                    grp.record = None

                if i == 0:
                    for index in channel_indexes:
                        signal = self.get(
                            group=group,
                            index=index,
                            data=fragment,
                            copy_master=False,
                            raw=True,
                            ignore_invalidation_bits=True,
                        )

                        sigs[index] = signal
                        signal_parts[(group, index)].append(
                            (signal.samples, signal.invalidation_bits)
                        )

                    master_parts.append(signal.timestamps)
                else:
                    for index in channel_indexes:
                        signal = self.get(
                            group=group,
                            index=index,
                            data=fragment,
                            copy_master=False,
                            raw=True,
                            samples_only=True,
                            ignore_invalidation_bits=True,
                        )

                        signal_parts[(group, index)].append(signal)

                    master_parts.append(
                        self.get_master(
                            group,
                            data=fragment,
                            copy_master=False,
                        )
                    )
                grp.record = None

            pieces = len(master_parts)
            if pieces > 1:
                master = np.concatenate(master_parts)
            else:
                master = master_parts[0]
            master_parts = None
            pairs = list(signal_parts.keys())
            for pair in pairs:
                group, index = pair
                parts = signal_parts.pop(pair)
                sig = sigs.pop(index)

                if pieces > 1:
                    samples = np.concatenate(
                        [part[0] for part in parts]
                    )
                    if sig.invalidation_bits is not None:
                        invalidation_bits = np.concatenate(
                            [part[0] for part in parts]
                        )
                    else:
                        invalidation_bits = None
                else:
                    samples = parts[0][0]
                    if sig.invalidation_bits is not None:
                        invalidation_bits = parts[0][1]
                    else:
                        invalidation_bits = None

                signals[pair] = Signal(
                    samples=samples,
                    timestamps=master,
                    name=sig.name,
                    unit=sig.unit,
                    bit_count=sig.bit_count,
                    attachment=sig.attachment,
                    comment=sig.comment,
                    conversion=sig.conversion,
                    display_name=sig.display_name,
                    encoding=sig.encoding,
                    master_metadata=sig.master_metadata,
                    raw=True,
                    source=sig.source,
                    stream_sync=sig.stream_sync,
                    invalidation_bits=invalidation_bits,
                )

        signals = [
            signals[pair]
            for pair in indexes
        ]

        if copy_master:
            for signal in signals:
                signal.timestamps = signal.timestamps.copy()

        if not raw:
            for signal in signals:
                conversion = signal.conversion
                if conversion:
                    signal.samples = conversion.convert(signal.samples)
                raw = False
                signal.conversion = None

        if dataframe:

            interpolation_mode = self._integer_interpolation
            times = [s.timestamps for s in signals]
            master = reduce(np.union1d, times).flatten().astype(np.float64)
            signals = [
                s.interp(master, interpolation_mode=interpolation_mode)
                for s in signals
            ]

            df = pd.DataFrame()

            df["time"] = pd.Series(master, index=np.arange(len(master)))

            df.set_index('time', inplace=True)

            used_names = UniqueDB()
            used_names.get_unique_name("time")

            for k, sig in enumerate(signals):
                # byte arrays
                if len(sig.samples.shape) > 1:
                    arr = [sig.samples]
                    types = [(sig.name, sig.samples.dtype, sig.samples.shape[1:])]
                    sig.samples = np.core.records.fromarrays(arr, dtype=types)

                    channel_name = used_names.get_unique_name(sig.name)
                    df[channel_name] = pd.Series(sig.samples, index=master, dtype="O")

                # arrays and structures
                elif sig.samples.dtype.names:
                    for name, series in components(sig.samples, sig.name, used_names, master=master):
                        df[name] = series

                # scalars
                else:
                    channel_name = used_names.get_unique_name(sig.name)
                    df[channel_name] = pd.Series(sig.samples, index=master)

            return df
        else:
            return signals