saves the script data to a file
        filename: target filename, if not provided, it is created from internal function
        data_tag: string, if provided save only the data that matches the tag, otherwise save all data
        verbose: if true print additional info to std out
        Returns:

def save_data(self, filename = None, data_tag = None, verbose=False):
        """
        saves the script data to a file
        filename: target filename, if not provided, it is created from internal function
        data_tag: string, if provided save only the data that matches the tag, otherwise save all data
        verbose: if true print additional info to std out
        Returns:

        """


        def len(x):
            """
            overwrite the buildin len function to cover cases that don't have a length, like int or float
            and to catch string as objects of length 0
            Args:
                x: quantity of which we want to find the length
            Returns: length of x

            """
            if isinstance(x, (int, float, str)) or x is None:
                result = 0
            else:
                result = builtin_len(x)
            return result

        if filename is None:
            filename = self.filename('.csv')

        filename = os.path.join(os.path.join(os.path.dirname(filename),self.RAW_DATA_DIR), os.path.basename(filename))

        # windows can't deal with long filenames so we have to use the prefix '\\\\?\\'
        # if len(filename.split('\\\\?\\')) == 1:
        filename = self.check_filename(filename)

        if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))

        # if deque object, take the last dataset, which is the most recent
        if isinstance(self.data, deque):
            data = self.data[-1]
        elif isinstance(self.data, dict):
            data = self.data
        else:
            raise TypeError("script data variable has an invalid datatype! Must be deque or dict.")


        if data_tag is None:
            if verbose:
                print('data_tag is None')

            if len(set([len(v) for v in list(data.values())])) == 1 and set(
                    [len(np.shape(list(data.values())[i])) for i in range(len(list(data.values())))]) == set([0, 1]):
                # if all entries of the dictionary are the same length and single column we can write the data into a single file

                if len(np.shape(list(data.values())[0]))==1:
                    df = pd.DataFrame(data)
                else:
                    df = pd.DataFrame.from_records([data])

                if len(df) == 0 or df.empty:
                    print('warning! Data seems to be empty. Not saved', df)
                else:
                    df.to_csv(filename, index=False)

            else:
                # otherwise, we write each entry into a separate file
                for key, value in data.items():

                    if verbose:
                        print('current data', key)

                    if len(value) == 0:
                        df = pd.DataFrame([value])
                    else:
                        if isinstance(value, dict) and isinstance(list(value.values())[0], (int, float)):
                            # if dictionary values are single numbers
                            df = pd.DataFrame.from_dict({k: [v] for k, v in value.items()})
                        elif isinstance(value, dict) and isinstance(list(value.values())[0], (list, np.ndarray)):
                            # if dictionary values are lists or arrays
                            df = pd.DataFrame.from_dict(value)
                        else:
                            # if not a dictionary
                            df = pd.DataFrame(value)

                    if len(df) == 0 or df.empty:
                        print('warning! Data ({:s}) seems to be empty. Not saved'.format(key), df)
                    else:
                        df.to_csv(filename.replace('.csv', '-{:s}.csv'.format(key)), index=False)

        else:

            # save only the data for which a key has been provided
            assert data_tag in list(data.keys())

            if verbose:
                print('data_tag', data_tag)

            value = data[data_tag]
            if len(value) == 0:
                df = pd.DataFrame([value])
            else:
                df = pd.DataFrame(value)

            if len(df) == 0 or df.empty:
                print('warning! Data seems to be empty. Not saved', df)
            else:
                df.to_csv(filename, index=False)