<jupyter_start><jupyter_text>Basic Mapping[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/htcondor/htmap/master?urlpath=lab%2Ftree%2Fbasic-mapping.ipynb)  Tags In the previous tutorial, we used HTMap like this:<jupyter_code>import htmap

def double(x):
    return 2 * x
mapped = htmap.map(double, range(10))
print(mapped)
doubled = list(mapped)
print(doubled)<jupyter_output>Created map dark-puny-robe with 10 components
Map(tag = dark-puny-robe)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]<jupyter_text>In particular, we used the [htmap.map](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.map) function to create our map.This function creates an object that behaves a lot like the iterator returned by the built-in [map](https://docs.python.org/3/library/functions.htmlmap) function.To get our output, we iterated over it using `list`.You may have noticed that the map has a **tag** associated with it.HTMap generated this tag for us because we didn't provide one, and because we didn't provide one, marked the map as **transient**, as opposed to **persistent**.Transient maps are for quick tests where we don't care too much about organization.Persistent maps are for longer-running maps where we want to keep our work organized by giving things real names.If you don't plan on using your map for more than one session, you can probably get away with a transient map.If you're going to step away from the computer and come back, we recommend giving it a real tag.The map we created above is transient:<jupyter_code>print(mapped.is_transient)<jupyter_output>True<jupyter_text>To create a persistent map, we need to give our map our map a tag:<jupyter_code>another_map = htmap.map(double, range(10), tag = 'dbl')
print(another_map)
print(another_map.is_transient)<jupyter_output>Created map dbl with 10 components
Map(tag = dbl)
False<jupyter_text>We can also "retag" a map to give it a new tag.If you tag a transient map, it becomes persistent.<jupyter_code>mapped.retag('a-new-tag')
print(mapped)
print(mapped.is_transient)<jupyter_output>Map(tag = a-new-tag)
False<jupyter_text>Working with Maps The object that was returned by [htmap.map](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.map) is a [htmap.Map](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.Map).It gives us a window into the map as it is running, and lets us use the output once the map is finished.For example, we can print the status of the map:<jupyter_code>stringified = htmap.map(str, range(10), tag = 'str')
print(stringified.status())<jupyter_output>Created map str with 10 components
Map str (10 components): HELD = 0 | ERRORED = 0 | IDLE = 10 | RUNNING = 0 | COMPLETED = 0<jupyter_text>We can wait for the map to finish:<jupyter_code>stringified.wait(show_progress_bar = True)<jupyter_output>str: 100%|##########| 10/10 [00:09<00:00,  1.11component/s]<jupyter_text>There are many ways to iterate over maps:<jupyter_code>print(list(stringified))

for d in stringified:
    print(d)<jupyter_output>['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
0
1
2
3
4
5
6
7
8
9<jupyter_text>If we ever lose our reference to it, we can grab a new reference to it using [htmap.load](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.load), giving it the tag of the map we want:<jupyter_code>new_ref = htmap.load('str')

print(new_ref)
print(new_ref == stringified)
print(new_ref is stringified)  # maps are singletons<jupyter_output>Map(tag = str)
True
True<jupyter_text>Maps can be recovered from an entirely different Python interpreter session as well.Suppose you close Python and go on vacation.You come back and you want to look at your map again, but you've forgotten what you called it.Just ask HTMap for a list of your tags:<jupyter_code>print(htmap.get_tags())<jupyter_output>('dbl', 'str', 'a-new-tag')<jupyter_text>Ok, well, technically it was a tuple, but we'll have to live with it.HTMap can also print a pretty table showing the status of your maps:<jupyter_code>htmap.map(str, range(5))  # new transient map
print(htmap.status())<jupyter_output>Created map breezy-happy-hand with 5 components
Tag                  HELD  ERRORED  IDLE  RUNNING  COMPLETED  Local Data  Max Memory  Max Runtime  Total Runtime
a-new-tag             0       0      0       0         10      63.9 KB     41.0 MB      0:00:00       0:00:00   
dbl                   0       0      0       0         10      63.9 KB     41.0 MB      0:00:00       0:00:00   
str                   0       0      0       0         10      63.5 KB     41.0 MB      0:00:00       0:00:00   
* breezy-happy-hand   0       0      5       0         0       19.8 KB      0.0 B       0:00:00       0:00:00<jupyter_text>Note that transient maps have a `*` in front of their tags.The status message tells us about how many components of our map are in each of the five most common component states:* **Idle** - component is waiting to run* **Running** - component is currently executing remotely* **Completed** - component is finished executing and output is available* **Held** - HTCondor has noticed a problem with the component and is not letting it run* **Errored** - there was an error in your code, and HTMap has brought back error informationThe status of each component of your map is available using the map attribute `component_statuses`:<jupyter_code>print(new_ref.component_statuses)<jupyter_output>[<ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>, <ComponentStatus.COMPLETED: 'COMPLETED'>]<jupyter_text>We'll discuss what to do about held and errored components and how to interact with component statuses in the [Error Handling](error-handling.ipynb) tutorial. Tags are *unique*: if we try to create another map with a tag we've already used, it will fail:<jupyter_code>new_map = htmap.map(double, range(10), tag = 'dbl')<jupyter_output><empty_output><jupyter_text>As the error message indicates, if we want to re-use the tag `dbl`, we need to remove the old map first:<jupyter_code>old_map = htmap.load('dbl')
old_map.remove()<jupyter_output><empty_output><jupyter_text>[htmap.Map.remove](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.Map.remove) deletes all traces of the map.**It can never be recovered**.Be careful when using it!The module-level shortcut [htmap.remove](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.remove) lets you skip the intermediate step of getting the actual Map, if you don't already have it. Now we can re-use the map ID:<jupyter_code>new_map = htmap.map(double, range(10), tag = 'dbl')
new_map.wait(show_progress_bar = True)
print(list(new_map))<jupyter_output>dbl:   0%|          | 0/10 [00:00<?, ?component/s]<jupyter_text>Map BuildersSo far we've been avoiding any functions that needed to be mapped over keyword arguments, or that had more than one positional argument.`htmap.map` is not really the ideal tool for working with functions that have more than one argument, and it does not support varying more than one argument at all.A much more ergonomic way to build up a complex map is a **map builder**.A map builder lets you build a map via individual function calls.Call [htmap.build_map](https://htmap.readthedocs.io/en/stable/api.htmlhtmap.build_map) as a context manager to get the builder, then call the builder as if it were the mapped function itself:<jupyter_code>def power(base, exponent):
    return base ** exponent

with htmap.build_map(power) as pow_builder:
    for base in range(1, 5):  # bases are 1, 2, 3, 4
        for exponent in range(1, 4):  # exponents are 1, 2, 3
            pow_builder(base, exponent)

powered = pow_builder.map
print(list(powered))  # 1^1, 1^2, 1^3, 2^1, 2^2, 2^3, 3^1 ...<jupyter_output>Created map harsh-happy-ring with 12 components
[1, 1, 1, 2, 4, 8, 3, 9, 27, 4, 16, 64]
{'fraction_non_alphanumeric': 0.1040712783071402, 'fraction_numerical': 0.027224353421606236, 'mean_word_length': 4.409638554216867, 'pattern_counts': {'":': 0, '<': 60, '<?xml version=': 0, '>': 58, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '458202', 'license': 'Apache-2.0', 'n_tokens_mistral': 2787, 'n_tokens_neox': 2630, 'n_words': 925, 'path': 'docs/source/tutorials/basic-mapping.ipynb', 'repo_name': 'JoshKarpel/condormap', 'size': '8081'}