# Buggy code
def write_data_file(data, fn, sort_cols=False, data_format=None):
    """Write a data file to the report directory. Will not do anything
    if config.data_dir is not set.
    :param: data - a 2D dict, first key sample name (row header),
            second key field (column header).
    :param: fn - Desired filename. Directory will be prepended automatically.
    :param: sort_cols - Sort columns alphabetically
    :param: data_format - Output format. Defaults to config.data_format (usually tsv)
    :return: None"""

    if config.data_dir is not None:
        # Add relevant file extension to filename
        if data_format is None:
            data_format = config.data_format
        fn = "{}.{}".format(fn, config.data_format_extensions[data_format])

        # JSON encoder class to handle lambda functions
        class MQCJSONEncoder(json.JSONEncoder):
            def default(self, obj):
                if callable(obj):
                    try:
                        return obj(1)
                    except:
                        return None
                return json.JSONEncoder.default(self, obj)

        # Save file
        with io.open(os.path.join(config.data_dir, fn), "w", encoding="utf-8") as f:
            if data_format == "json":
                jsonstr = json.dumps(
                    data, indent=4, cls=MQCJSONEncoder, ensure_ascii=False
                )
                print(jsonstr.encode("utf-8", "ignore").decode("utf-8"), file=f)
            elif data_format == "yaml":
                yaml.dump(data, f, default_flow_style=False)
            else:
                # Default - tab separated output
                # Get all headers
                h = ["Sample"]
                for sn in sorted(data.keys()):
                    for k in data[sn].keys():
                        if type(data[sn][k]) is not dict and k not in h:
                            h.append(str(k))
                if sort_cols:
                    h = sorted(h)

                # Get the rows
                rows = ["\t".join(h)]
                for sn in sorted(data.keys()):
                    # Make a list starting with the sample name, then each field in order of the header cols
                    l = [str(sn)] + [str(data[sn].get(k, "")) for k in h[1:]]
                    rows.append("\t".join(l))

                body = "\n".join(rows)

                print(body.encode("utf-8", "ignore").decode("utf-8"), file=f)


# Traceback (TypeError)
Module custom_content raised an exception: Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/multiqc-1.8.dev0-py3.6.egg/EGG-INFO/scripts/multiqc", line 440, in multiqc
output = mod()
File "/usr/lib/python3.6/site-packages/multiqc-1.8.dev0-py3.6.egg/multiqc/modules/custom_content/custom_content.py", line 226, in custom_module_classes
parsed_modules.append( MultiqcModule(module_id, mod) )
File "/usr/lib/python3.6/site-packages/multiqc-1.8.dev0-py3.6.egg/multiqc/modules/custom_content/custom_content.py", line 272, in __init__
self.write_data_file( mod['data'], "multiqc_{}".format(modname.lower().replace(' ', '_')) )
File "/usr/lib/python3.6/site-packages/multiqc-1.8.dev0-py3.6.egg/multiqc/modules/base_module.py", line 332, in write_data_file
util_functions.write_data_file(data, fn, sort_cols, data_format)
File "/usr/lib/python3.6/site-packages/multiqc-1.8.dev0-py3.6.egg/multiqc/utils/util_functions.py", line 78, in write_data_file
for sn in sorted(data.keys()):
TypeError: '<' not supported between instances of 'str' and 'float'

# Fixed code
def write_data_file(data, fn, sort_cols=False, data_format=None):
    """Write a data file to the report directory. Will not do anything
    if config.data_dir is not set.
    :param: data - a 2D dict, first key sample name (row header),
            second key field (column header).
    :param: fn - Desired filename. Directory will be prepended automatically.
    :param: sort_cols - Sort columns alphabetically
    :param: data_format - Output format. Defaults to config.data_format (usually tsv)
    :return: None"""

    if config.data_dir is not None:
        # Add relevant file extension to filename
        if data_format is None:
            data_format = config.data_format
        fn = "{}.{}".format(fn, config.data_format_extensions[data_format])

        # JSON encoder class to handle lambda functions
        class MQCJSONEncoder(json.JSONEncoder):
            def default(self, obj):
                if callable(obj):
                    try:
                        return obj(1)
                    except:
                        return None
                return json.JSONEncoder.default(self, obj)

        # Save file
        with io.open(os.path.join(config.data_dir, fn), "w", encoding="utf-8") as f:
            if data_format == "json":
                jsonstr = json.dumps(
                    data, indent=4, cls=MQCJSONEncoder, ensure_ascii=False
                )
                print(jsonstr.encode("utf-8", "ignore").decode("utf-8"), file=f)
            elif data_format == "yaml":
                yaml.dump(data, f, default_flow_style=False)
            else:
                # Default - tab separated output
                # Convert keys to strings
                data = {str(k): v for k, v in data.items()}
                # Get all headers
                h = ["Sample"]
                for sn in sorted(data.keys()):
                    for k in data[sn].keys():
                        if type(data[sn][k]) is not dict and k not in h:
                            h.append(str(k))
                if sort_cols:
                    h = sorted(h)

                # Get the rows
                rows = ["\t".join(h)]
                for sn in sorted(data.keys()):
                    # Make a list starting with the sample name, then each field in order of the header cols
                    l = [str(sn)] + [str(data[sn].get(k, "")) for k in h[1:]]
                    rows.append("\t".join(l))

                body = "\n".join(rows)

                print(body.encode("utf-8", "ignore").decode("utf-8"), file=f)