はじめに
分析対象データにHEX(16進数)データがある場合、基本統計量や傾向を確認するためには、10進数に変換しなければなりません。
できればPythonで、HEXだけのデータカラムを自動で判定し、10進数に自動変換させたいということで、実行してみました。
実行条件など
- Google colabで実行
まずはデータを準備
16進数⇒10進数変換を試行するデータを仮想、以下の通りデータフレームに格納しました。
aはint、cはfloat、b,fがHEX(16進数)データ、dはobject、eはint(ほぼ欠損データ)としています。
16進数⇒10進数変換したいカラムは b列,f列です。
df = pd.DataFrame({
'a': [5, 2, 3, 100, 110, 200],
'b': ['abc', '0C3A', '9F07D2','ffff','ffff','7b2'],
'c': [19.3, 22.7, 99.5, 200.2, 300.5, 1200.3],
'd': ['A', '100', 'a','abc','AB','ZA-20'],
'e': [1, np.nan, np.nan, np.nan, np.nan, np.nan],
'f': ['abc', '0C3A', '9F07D2','ffff','ffff','7b2']
})
print(df)
index | a | b | c | d | e | f |
---|---|---|---|---|---|---|
0 | 5 | abc | 19.3 | A | 1.0 | abc |
1 | 2 | 0C3A | 22.7 | 100 | NaN | 0C3A |
2 | 3 | 9F07D2 | 99.5 | a | NaN | 9F07D2 |
3 | 100 | ffff | 200.2 | abc | NaN | ffff |
4 | 110 | ffff | 300.5 | AB | NaN | ffff |
5 | 200 | 7b2 | 1200.3 | ZA-20 | NaN | 7b2 |
HEX⇒INT処理をデータフレーム全体に実行すると?
def hex_to_int(s):
try:
return int(s, base=16)
except:
return s
dfx = df.applymap(hex_to_int)
dfx
元データ
index | a | b | c | d | e | f |
---|---|---|---|---|---|---|
0 | 5 | abc | 19.3 | A | 1.0 | abc |
1 | 2 | 0C3A | 22.7 | 100 | NaN | 0C3A |
2 | 3 | 9F07D2 | 99.5 | a | NaN | 9F07D2 |
3 | 100 | ffff | 200.2 | abc | NaN | ffff |
4 | 110 | ffff | 300.5 | AB | NaN | ffff |
5 | 200 | 7b2 | 1200.3 | ZA-20 | NaN | 7b2 |
変換後データ
index | a | b | c | d | e | f |
---|---|---|---|---|---|---|
0 | 5 | 2748 | 19.3 | 10 | 1.0 | 2748 |
1 | 2 | 3130 | 22.7 | 256 | NaN | 3130 |
2 | 3 | 10422226 | 99.5 | 10 | NaN | 10422226 |
3 | 100 | 65535 | 200.2 | 2748 | NaN | 65535 |
4 | 110 | 65535 | 300.5 | 171 | NaN | 65535 |
5 | 200 | 1970 | 1200.3 | ZA-20 | NaN | 1970 |
上記が変換結果です。
b,f列は意図通り10進数に変換され、int, float列はそのまま維持されていますが、d列(object)の ’A’、’100’、’abc’等がHEX(16進数)と認識され、変換されてしまいます。
HEX⇒INT変換するカラムを指定してみる
上記のままではまずいので、10進数に変換するカラムを指定して実行してみます。
Object_label_to = 'b','f' #@param {type:"raw"}
for i in Object_label_to:
if df[col].dtype == 'object':
df[i]=df[i].apply(lambda x:int(x, 16))
print(df)
index | a | b | c | d | e | f |
---|---|---|---|---|---|---|
0 | 5 | 2748 | 19.3 | A | 1.0 | 2748 |
1 | 2 | 3130 | 22.7 | 100 | NaN | 3130 |
2 | 3 | 10422226 | 99.5 | a | NaN | 10422226 |
3 | 100 | 65535 | 200.2 | abc | NaN | 65535 |
4 | 110 | 65535 | 300.5 | AB | NaN | 65535 |
5 | 200 | 1970 | 1200.3 | ZA-20 | NaN | 1970 |
これで、意図した変換ができました。
string.hexdigits を利用してみる
ただ、カラムの指定はデータフレームサイズが大きくなるほど面倒なので、カラムデータが16進数であるかどうかを判定してみます。
以下を実行すると、16進数の要素を返してくれます。
import string
string.hexdigits
0123456789abcdefABCDEF
このstringを利用して、データフレームの各カラムが16進数データかどうかを確認してみます。
for col in df.columns:
if df[col].dtype == 'object': #int,floatカラムに実行するとエラーになるのでobject指定
print(col)
result=df[col].map(lambda x: set(x).issubset(set(string.hexdigits)))
print(result)
b
0 True
1 True
2 True
3 True
4 True
5 True
Name: b, dtype: bool
d
0 True
1 True
2 True
3 True
4 True
5 False
Name: d, dtype: bool
f
0 True
1 True
2 True
3 True
4 True
5 True
Name: f, dtype: bool
上記は、object列であるb,d,f列のみ、データが16進数であるかが判定された結果です。
d列のNo.5データ(ZA-20)のみ、False(16進数ではない)と判定されています。
次にカラム全体が16進数データであるかを判定します。
for col in df.columns:
if df[col].dtype == 'object':
if all(df[col].apply(lambda x: set(x).issubset(set(string.hexdigits)))) ==True:
print(col,True)
else:
print(col,False)
b True
d False
f True
これで、16進数だけで構成されているb列、f列だけをピックアップすることができました。
16進数だけのカラムデータを自動判定し、10進数に変換
for col in df.columns:
if df[col].dtype == 'object':
if all(df[col].apply(lambda x: set(x).issubset(set(string.hexdigits)))) ==True:
df[col]=df[col].apply(lambda x:int(x, 16))
print(df)
index | a | b | c | d | e | f |
---|---|---|---|---|---|---|
0 | 5 | 2748 | 19.3 | A | 1.0 | 2748 |
1 | 2 | 3130 | 22.7 | 100 | NaN | 3130 |
2 | 3 | 10422226 | 99.5 | a | NaN | 10422226 |
3 | 100 | 65535 | 200.2 | abc | NaN | 65535 |
4 | 110 | 65535 | 300.5 | AB | NaN | 65535 |
5 | 200 | 1970 | 1200.3 | ZA-20 | NaN | 1970 |
いけました。
ところが、現実データを確認するとあまりにゴミデータが多い。。。
16進数だけのカラムデータというのは、ありえない気がしてきましたので、結局、16進数のカラムを指定し、16進数データのみ10進数に変換することに。
16進数のカラムを指定し、16進数データのみ10進数に変換
HEX2INT = 'b1', 'f1' # @param {type: "raw"}
HEX2INT = pd. Series(HEX2INT)
import string
for i in HEX2INT:
if df[i].dtype == 'object':
for x in df[i]:
try:
if set (x).issubset(set(string.hexdigits)) ==True
df[i].replace(x, int(x, 16), inplace=True)
except:
print('例外',i,x)
‘abc’,‘---’,‘fail’,‘ffff’,‘NaN’,‘7b2’というデータならば、
‘2738’,‘---’,‘fail’,‘65535’,‘NaN’,‘1970’となります。
isdigitで数字以外を nanにするか、削除するかなど、もう一段処理は必要ですが。
参考サイト