<jupyter_start><jupyter_text>I first replace any non-alphanumeric characters and then group the outlying minor battery types into one category - 'others'.I want to take a closer look at https://batteryuniversity.com/learn/article/types_of_battery_cells and estimate a device lifespan<jupyter_code>def transform(df):
    def branch_ver(x):
        m = re.search(r'[0-9_]', x)
        idx = m.span()[0] if m is not None else len(x)
        return x[:idx]

    t = df[['Census_OSBranch', 'OsPlatformSubRelease']].copy()
    t.columns = ['branch', 'subrel']

    t.branch = t.branch.apply(lambda x: x.replace('release', ''))
    t['branch_ver'] = [branch_ver(x) for x in t.branch]
    t['subrel_ver'] = [branch_ver(x) for x in t.subrel]

    t['subrel_ver_num'] = [re.sub(r'[^0-9.]', '', c) for c in t.subrel]
    t['subrel_ver_num'] = [
        np.round(float(x), 1) if isfloat(x) else np.nan for x in t.subrel_ver_num
    ]

    t['branch_release_num'] = [re.sub(r'[^0-9.]', '', c) for c in t.branch] 
    t['branch_release_num'] = [
        np.round(float(x[0]), 1) if len(x) > 0 and isfloat(x[0]) else np.nan for x in t.branch_release_num
    ]

    t['is_svc_release'] = ['svc' in c for c in t.branch]
    t['is_escrow_release'] = ['escrow' in c for c in t.branch]
    t['is_sec_release'] = ['sec' in c for c in t.branch]
    t['is_st1_release'] = ['st1' in c for c in t.branch]
    t['is_prerelease'] = ['pre' in c for c in t.branch]
    t['is_special_release'] = [
        any([y in c for y in ['flt', 'sigma', 'edge']]) 
        for c in t.branch
    ]

    t.loc[t.subrel_ver == 'prers', 'is_prerelease'] = True
    t.loc[t.subrel_ver == 'prers', 'subrel_ver'] = 'rs'

    t.loc[['win' in c for c in t.branch_ver], 'branch_ver'] = 'win'

    t.loc[t.branch_release_num.isnull(), 'branch_release_num'] = 0

    t.drop(columns=['branch', 'subrel'], inplace=True)
    t.head()
    t.branch_ver.value_counts()
    t.subrel_ver.value_counts()

    for c in t.columns:
        df[c] = t[c]

    df = df.drop(columns=['Census_OSBranch', 'OsPlatformSubRelease'])
    
    return df

train = transform(train)
test = transform(test)
avsig = pipe.download_feature('AvSigVersion_float', cache=True)
avsig_combined = pd.concat([avsig['train'], avsig['validate']], sort=False).sort_values(by='AvSigVersion_float')

train_ = train.merge(avsig_combined, on='MachineIdentifier', how='inner', sort=False)
assert len(train_) == len(train)

train = train_
del train_

train = train.sort_values(by='AvSigVersion_float').drop(columns='AvSigVersion_float')
train.head()
test.head()
val_idx = int(len(train)*.7)
val_idx
pbar = tqdm(total=len(train.columns)-2)

for c in train.columns:
    if c == 'MachineIdentifier' or c == 'Target':
        continue
        
    pbar.set_description(c)
    
    train_ = train[['MachineIdentifier', c]].iloc[:val_idx, :]
    val_   = train[['MachineIdentifier', c]].iloc[val_idx:, :]
    test_  =  test[['MachineIdentifier', c]]
    
    try:
        pipe.upload_feature(c, (train_, val_, test_), overwrite=True)
    except ValueError:
        pass
    
    pbar.update(1)<jupyter_output>is_special_release: 100%|██████████| 10/10 [21:07<00:00, 123.24s/it]
{'fraction_non_alphanumeric': 0.16861014812480302, 'fraction_numerical': 0.011976047904191617, 'mean_word_length': 3.613372093023256, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '549725', 'license': 'MIT', 'n_tokens_mistral': 1234, 'n_tokens_neox': 1192, 'n_words': 267, 'path': 'notebooks/feature_engineering/OSPlatform_and_Census_OSBranch.ipynb', 'repo_name': 'lukeWaninger/DankDefense', 'size': '3173'}