<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 This notebook demonstrates vaquero, as both a library and data cleaning pattern.
Step1:  Task
Say you think you have pairs of numbers serialized as comma separated values in a file. You want to extract the pair from each line, then sum over the result (per line). 
Sample Data
Step2:  Initial Implementation
Step3:  Iteration 1
First, instantiate a vaquero instance. Here, I've set the maximum number of failures allowed to 5. After that many failures, the Vaquero object raises a VaqueroException. Generally, you want it to be large enough to collect a lot of unexpected failures. But, you don't want it to be so large you exhaust memory. This is an iterative process.
Also, as a tip, always instantiate the Vaquero object in its own cell. This way, you get to inspect it in your notebook even if it raises a VaqueroException.
I also registered all functions (well, callables) in this notebook with vaquero. The error capturing machinery only operates on the registered functions. And, it always ignores a KeyboardInterrupt.
Step4:  Just to be sure, I'll check the registered functions. It does matching by name, which is a bit naive. But, it's also surprisingly robust given vaquero usage patterns. Looking, you can see some things that don't belong. But, again, it mostly works well.
Step5:  Now, run my trivial examples over the initial implementation.
Step6:  It was not successful.
Step7:  So, look at the failures. There were two functions, and both had failures.
Step8:  To get a sense of what happened, examine the failing functions.
You can do this by calling examine with the name of the function (or the function object). It returns the captured invocations and errors.
Here you can see that the to_int function from cell In [3] failed with a ValueError exception.
Step9:  Often though, we want to query only parts of the capture for a specific function. To do so, you can use JMESPath, specifying the selector as an argument to exam. Also, you can say, show me only the set applied to the selected result (assuming it's hashable), to simplify things.
Step10:  And, for sum_pair.
Step11:  Iteration 2
We know know that there are some ints encoded as doubles. But, we know from our data source, it can only be an int. So, in to_ints, let's parse the strings first as floats, then create an int from it. It's robust.
Also, we know that some lines don't have two components. Those are just bad lines. Let's assert there are two parts as  post condition of extract_pairs. 
Finally, after a bit of digging, we found that $ means NA. After cursing for a minute -- because that's crazy -- you decide to ignore those entries. Instead of adding this to an existing function, you write an assert_no_missing_data function.
Step12:  Now, we have one more success, but still two failures.
Step13:  Let's quickly examine.
Step14:  Both these exceptions are bad data. We want to ignore them.
Step15:  Looking at the results accumulated,
Step16:  Things look good.
Now that we have something that works, we can use Vaquero in a more production-oriented mode. That is, we allow for unlimited errors, but we don't capture anything. That is, we note the failure, but otherwise ignore it since we won't be post-processing.
Step17:  They still show up as failures, but it doesn't waste memory storing the captures.
<ASSISTANT_TASK:>
Python Code:
from vaquero import Vaquero, callables_from
Explanation: This notebook demonstrates vaquero, as both a library and data cleaning pattern.
End of explanation
lines = ["1, 1.0",  # An errant float
         "1, $",    # A bad number
         "1,-1",    # A good line
         "10"]      # Missing the second value
Explanation: Task
Say you think you have pairs of numbers serialized as comma separated values in a file. You want to extract the pair from each line, then sum over the result (per line). 
Sample Data
End of explanation
def extract_pairs(s):
    return s.split(",")
    
    
def to_int(items):
    return [int(item) for item in items]
def sum_pair(items):
    return items[0], items[1]
Explanation: Initial Implementation
End of explanation
vaquero = Vaquero(max_failures=5)
vaquero.register_targets(callables_from(globals()))
Explanation: Iteration 1
First, instantiate a vaquero instance. Here, I've set the maximum number of failures allowed to 5. After that many failures, the Vaquero object raises a VaqueroException. Generally, you want it to be large enough to collect a lot of unexpected failures. But, you don't want it to be so large you exhaust memory. This is an iterative process.
Also, as a tip, always instantiate the Vaquero object in its own cell. This way, you get to inspect it in your notebook even if it raises a VaqueroException.
I also registered all functions (well, callables) in this notebook with vaquero. The error capturing machinery only operates on the registered functions. And, it always ignores a KeyboardInterrupt.
End of explanation
vaquero.target_funcs
Explanation: Just to be sure, I'll check the registered functions. It does matching by name, which is a bit naive. But, it's also surprisingly robust given vaquero usage patterns. Looking, you can see some things that don't belong. But, again, it mostly works well.
End of explanation
results = []
for s in lines:
    with vaquero.on_input(s):
        results.append(sum_pair(to_int(extract_pairs(s))))
Explanation: Now, run my trivial examples over the initial implementation.
End of explanation
vaquero.was_successful
Explanation: It was not successful.
End of explanation
vaquero.stats()
Explanation: So, look at the failures. There were two functions, and both had failures.
End of explanation
vaquero.examine('to_int')
Explanation: To get a sense of what happened, examine the failing functions.
You can do this by calling examine with the name of the function (or the function object). It returns the captured invocations and errors.
Here you can see that the to_int function from cell In [3] failed with a ValueError exception.
End of explanation
vaquero.examine('to_int', '[*].exc_value', as_set=True)
Explanation: Often though, we want to query only parts of the capture for a specific function. To do so, you can use JMESPath, specifying the selector as an argument to exam. Also, you can say, show me only the set applied to the selected result (assuming it's hashable), to simplify things.
End of explanation
vaquero.examine('sum_pair')
Explanation: And, for sum_pair.
End of explanation
def no_missing_data(s):
    assert '$' not in s, "'{}' has missing data".format(s)
    
    
def extract_pairs(s):
    parts = s.split(",")
    assert len(parts) == 2, "'{}' not in 2 parts".format(s)
    return tuple(parts)
    
    
def to_int(items):
    return [int(float(item)) for item in items]
def sum_pair(items):
    assert len(items) == 2, "Line is improperly formatted"
    return items[0] + items[1]
vaquero.reset()  # Clear logged errors, mostly.
vaquero.register_targets(globals())
results = []
for s in lines:
    with vaquero.on_input(s):
        no_missing_data(s)
        results.append(sum_pair(to_int(extract_pairs(s))))
Explanation: Iteration 2
We know know that there are some ints encoded as doubles. But, we know from our data source, it can only be an int. So, in to_ints, let's parse the strings first as floats, then create an int from it. It's robust.
Also, we know that some lines don't have two components. Those are just bad lines. Let's assert there are two parts as  post condition of extract_pairs. 
Finally, after a bit of digging, we found that $ means NA. After cursing for a minute -- because that's crazy -- you decide to ignore those entries. Instead of adding this to an existing function, you write an assert_no_missing_data function.
End of explanation
vaquero.stats()
Explanation: Now, we have one more success, but still two failures.
End of explanation
vaquero.examine('extract_pairs')
vaquero.examine('no_missing_data')
Explanation: Let's quickly examine.
End of explanation
vaquero.stats_ignoring('AssertionError')
Explanation: Both these exceptions are bad data. We want to ignore them.
End of explanation
results
Explanation: Looking at the results accumulated,
End of explanation
vaquero.reset(turn_off_error_capturing=True) 
# Or, Vaquero(capture_error_invocations=False)
results = []
for s in lines:
    with vaquero.on_input(s):
        no_missing_data(s)
        results.append(sum_pair(to_int(extract_pairs(s))))
        
results
Explanation: Things look good.
Now that we have something that works, we can use Vaquero in a more production-oriented mode. That is, we allow for unlimited errors, but we don't capture anything. That is, we note the failure, but otherwise ignore it since we won't be post-processing.
End of explanation
vaquero.stats()
Explanation: They still show up as failures, but it doesn't waste memory storing the captures.
End of explanation
<END_TASK>