Search code examples
pythonpandasparsinglambdavin

pyvin: Parsing VIN number


Context

Same code for decoding a column with Vehicle Identification Numbers, for one of the libraries i'm getting an error.

Code

import pandas as pd
from vininfo import Vin # COUNTRY AND BRAND
from pyvin import VIN # MODEL AND YEAR


db = pd.DataFrame("VIN": ["3N6PD23W5ZK911765", "MNTACUD40Z0000632", "3N6DD23T9ZK874454"])  # VIN EXAMPLE


db["COUNTRY"] = db["VIN"].map(lambda x: Vin(x).country)  # PARSES OK AND RETURNS COUNTRY
db["BRAND"] = db["VIN"].map(lambda x: Vin(x).manufacturer)  # PARSES OK AND RETURNS BRAND

db["MODEL"] = db["VIN"].map(lambda x: VIN(x).Model)  # ERROR
db["YEAR"] = db["VIN"].map(lambda x: VIN(x).ModelYear)  # ERROR

ERROR

AttributeError: 'list' object has no attribute 'Model'
or
AttributeError: 'list' object has no attribute 'ModelYear'

Question

I don't ask for a complete solution because the problem is pretty specific but at this point i'm feeling insecure and any tip is welcome.

Is x in db["YEAR"] = db["VIN"].map(lambda x: VIN(x).ModelYear) equal to a single string or is it passing a list to the function?


Solution

  • Let's break the problem in pieces. First, the db is a pandas.DataFrame object. The db["VIN"] is a pandas.Series object, as you probably know. You can also verify it with

    In [19]: type(db["VIN"])
    Out[19]: pandas.core.series.Series
    

    Then, the pandas.Series.map function that you are using takes arg as argument that can be type of dict or a function (or Series). You give it a function. Then, this function is called for each of the values of the series db["VIN"]. Let's see what that is.

    # first value OK. Maybe?
    In [23]: VIN("3N6PD23W5ZK911765")
    Out[23]: <pyvin.pyvin.DecodedVIN at 0x2ef77ea5550>
    
    # second value not OK
    In [24]: VIN("MNTACUD40Z0000632")
    Out[24]: []
    

    So, for some reason, the VIN returns a list. Why is that? It is because the pyvin.VIN does not recognize the input. If you add error handling, you'll see:

    In [26]: import pyvin
    
    In [27]: VIN("MNTACUD40Z0000632", error_handling=pyvin.RAISE)
    
    ...
    c:\python\python 3.8.6-64\lib\site-packages\pyvin\utils.py in _compare_check_digit(vin, remainder)
         92     if check_digit != rem_val:
         93         msg = 'Invalid check digit! %s does not match computed val %s' % (check_digit, rem_val)
    ---> 94         raise VINError(msg)
         95     logger.debug("%s check [OK]")
         96
    
    VINError: Invalid check digit! 0 does not match computed val 5
    

    I would suspect that either the VIN code is incorrect, the pyvin has a bug, or the underlying service (NHTSA API) does not recognize the VIN code.