@kimukazu1029 (和貴 木村)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

[初心者です]タイタニック号でのobject has no attribute のエラー

タイタニック号で精度80パーセント以上を出す課題が出ています。
データセットはkaggleのものとは違い,欠損値が?になっていたり,'age', 'pclass'と小文字になっていたりと少し違うものになっています。インターネットに掲載されていたものhttps://qiita.com/jun40vn/items/d8a1f71fae680589e05c 
を参考に書いてみたのですが,画像のエラーが出てしまう状態です。原因をご教示していただけますと嬉しいです。(皆さんみたいに綺麗なコードの貼り方がわからず。。。見にくくて申し訳ないです。)

訓練データ:https://drive.google.com/uc?export=download&id=1-12Pg5IsjNAEbgk7G4a2WqFaOhpYmbyJ
テストデータ:https://drive.google.com/uc?export=download&id=1jmzmYNPRWUGLcHeqhcwKTGlb_d2Rzn4Y

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

学習用のデータのダウンロード

!wget 'https://drive.google.com/uc?export=download&id=1-12Pg5IsjNAEbgk7G4a2WqFaOhpYmbyJ' -O titanic_train.csv

提出用のデータをダウンロード

!wget 'https://drive.google.com/uc?export=download&id=1-1KX2NQmwUOXAstOE7c2INC1rRMNABVZ' -O titanic_test_x.csv

train_data = pd.read_csv('titanic_train.csv', index_col=0)
test_data = pd.read_csv('titanic_test_x.csv', index_col=0)

#空いた部分に中央値を入れて埋める
train_data['name'].fillna(freq_port, inplace=True)
test_data['name'].fillna(freq_port, inplace=True)

train_dataとtest_dataの連結

test_data['survived'] = np.nan
df = pd.concat([train_data, test_data], ignore_index=True, sort=False)

dfの情報

df.info()

Sexと生存率の関係

sns.barplot(x='sex', y='survived', data=df, palette='Set3')
plt.show()

------------ Age ------------

Age を Pclass, Sex, Parch, SibSp からランダムフォレストで推定

from sklearn.ensemble import RandomForestRegressor

推定に使用する項目を指定

age_df = df[['age', 'pclass','sex','parch','sibsp']]

ラベル特徴量をワンホットエンコーディング

age_df=pd.get_dummies(age_df)

学習データとテストデータに分離し、numpyに変換

freq_port = train_data.age.dropna().mode()[0]
known_age = age_df[age_df.age.notnull()].values
unknown_age = age_df[age_df.age.isnull()].values

学習データをX, yに分離

X = known_age[:, 1:]
y = known_age[:, 0]

ランダムフォレストで推定モデルを構築

rfr = RandomForestRegressor(random_state=0, n_estimators=100, n_jobs=-1)
rfr.fit(X, y)

推定モデルを使って、テストデータのAgeを予測し、補完

predictedAges = rfr.predict(unknown_age[:, 1::])
df.loc[(df.age.isnull()), 'age'] = predictedAges

年齢別生存曲線と死亡曲線

facet = sns.FacetGrid(df[0:890], hue="survived",aspect=2)
facet.map(sns.kdeplot,'age',shade= True)
facet.set(xlim=(0, df.loc[0:890,'age'].max()))
facet.add_legend()
plt.show()

------------ Name --------------

Nameから敬称(Title)を抽出し、グルーピング

df['title'] = df['name'].map(lambda x: x.split(', ')[1].split('. ')[0])
df['title'].replace(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer', inplace=True)
df['title'].replace(['Don', 'Sir', 'the Countess', 'Lady', 'Dona'], 'Royalty', inplace=True)
df['title'].replace(['Mme', 'Ms'], 'Mrs', inplace=True)
df['title'].replace(['Mlle'], 'Miss', inplace=True)
df['title'].replace(['Jonkheer'], 'Master', inplace=True)
sns.barplot(x='title', y='survived', data=df, palette='Set3')

------------ Surname ------------

NameからSurname(苗字)を抽出

df['surname'] = df['name'].map(lambda name:name.split(',')[0].strip())

同じSurname(苗字)の出現頻度をカウント(出現回数が2以上なら家族)

df['familyGroup'] = df['surname'].map(df['surname'].value_counts())

家族で16才以下または女性の生存率

Female_Child_Group=df.loc[(df['familygroup']>=2) & ((df['age']<=16) | (df['sex']=='female'))]
Female_Child_Group=Female_Child_Group.groupby('surname')['survived'].mean()
print(Female_Child_Group.value_counts())

家族で16才超えかつ男性の生存率

Male_Adult_Group=df.loc[(df['familygroup']>=2) & (df['age']>16) & (df['sex']=='male')]
Male_Adult_List=Male_Adult_Group.groupby('surname')['survived'].mean()
print(Male_Adult_List.value_counts())

デッドリストとサバイブリストの作成

Dead_list=set(Female_Child_Group[Female_Child_Group.apply(lambda x:x==0)].index)
Survived_list=set(Male_Adult_List[Male_Adult_List.apply(lambda x:x==1)].index)

デッドリストとサバイブリストの表示

print('dead_list = ', Dead_list)
print('survived_list = ', Survived_list)

デッドリストとサバイブリストをSex, Age, Title に反映させる

df.loc[(df['survived'].isnull()) & (df['surname'].apply(lambda x:x in Dead_list)),
['sex','age','title']] = ['male',28.0,'Mr']
df.loc[(df['survived'].isnull()) & (df['Surname'].apply(lambda x:x in Survived_list)),
['sex','age','title']] = ['female',5.0,'Mrs']

----------- Fare -------------

欠損値を Embarked='S', Pclass=3 の平均値で補完

fare=df.loc[(df['embarked'] == 'S') & (df['pclass'] == 3), 'fare'].median()
df['fare']=df['fare'].fillna(fare)

----------- Family -------------

Family = SibSp + Parch + 1 を特徴量とし、グルーピング

df['family']=df['sibSp']+df['parch']+1
df.loc[(df['family']>=2) & (df['family']<=4), 'family_label'] = 2
df.loc[(df['family']>=5) & (df['family']<=7) | (df['family']==1), 'family_label'] = 1 # == に注意
df.loc[(df['family']>=8), 'family_label'] = 0

----------- Ticket ----------------

同一Ticketナンバーの人が何人いるかを特徴量として抽出

Ticket_Count = dict(df['ticket'].value_counts())
df['ticketGroup'] = df['ticket'].map(Ticket_Count)
sns.barplot(x='ticketGroup', y='survived', data=df, palette='Set3')
plt.show()

生存率で3つにグルーピング

df.loc[(df['ticketGroup']>=2) & (df['ticketGroup']<=4), 'Ticket_label'] = 2
df.loc[(df['ticketGroup']>=5) & (df['ticketGroup']<=8) | (df['ticketGroup']==1), 'ticket_label'] = 1
df.loc[(df['ticketGroup']>=11), 'ticket_label'] = 0
sns.barplot(x='ticket_label', y='survived', data=df, palette='Set3')
plt.show()

------------- Cabin ----------------

Cabinの先頭文字を特徴量とする(欠損値は U )

df['cabin'] = df['cabin'].fillna('Unknown')
df['cabin_label']=df['cabin'].str.get(0)
sns.barplot(x='cabin_label', y='survived', data=df, palette='Set3')
plt.show()

---------- Embarked ---------------

欠損値をSで補完

df['embarked'] = df['embarked'].fillna('S')

------------- 前処理 ---------------

推定に使用する項目を指定

df = df[['survived','pclass','sex','age','fare','embarked','title','family_label','cabin_label','ticket_label']]

ラベル特徴量をワンホットエンコーディング

df = pd.get_dummies(df)

データセットを trainとtestに分割

train = df[df['survived'].notnull()]
test = df[df['survived'].isnull()].drop('survived',axis=1)

データフレームをnumpyに変換

X = train.values[:,1:]
y = train.values[:,0]
test_x = test.values

----------- 推定モデル構築 ---------------

from sklearn.feature_selection import SelectKBest
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_validate

採用する特徴量を25個から20個に絞り込む

select = SelectKBest(k = 20)

clf = RandomForestClassifier(random_state = 10,
warm_start = True, # 既にフィットしたモデルに学習を追加
n_estimators = 26,
max_depth = 6,
max_features = 'sqrt')
pipeline = make_pipeline(select, clf)
pipeline.fit(X, y)

フィット結果の表示

cv_result = cross_validate(pipeline, X, y, cv= 10)
print('mean_score = ', np.mean(cv_result['test_score']))
print('mean_std = ', np.std(cv_result['test_score']))

-------- 採用した特徴量 ---------------

採用の可否状況

mask= select.get_support()

項目のリスト

list_col = list(df.columns[1:])

項目別の採用可否の一覧表

for i, j in enumerate(list_col):
print('No'+str(i+1), j,'=', mask[i])

シェイプの確認

X_selected = select.transform(X)
print('X.shape={}, X_selected.shape={}'.format(X.shape, X_selected.shape))

----- Submit dataの作成 -------

name=test_data["name"]
predictions = pipeline.predict(test_x)
submission = pd.DataFrame({"name": name, "name": predictions.astype(np.int32)})
submission.to_csv("my_submission.csv", index=False)

スクリーンショット 2022-08-07 14.18.38.png

0 likes

1Answer

はじめに

コードを記述する際は,Qiitaの質問のテンプレートにもあるとおり,バッククォート`を用いて囲むことでコードを綺麗に記述することができます.具体的には

```言語名:タイトル
コード本文
```

のようにして書けばよいです.詳しくは公式の解説を読んでください.

また,エラー文も画像ではなく上のようにコードブロックに記述していただけるとありがたいです.

エラーの解決法

Qiitaの質問のテンプレートの最後には「自分で試したこと」の欄があります.今回,kimukazuさんは解決にあたって何も取り組まず,問題を丸投げしているようにみえるので気を付けましょう.質問側が取り組んだことに対して解答側の指摘が重複してしまうのも避けたいですね.

本題です.普通,エラーが出た場合はどの行でエラーが発生したか,どのような内容のエラーなのかを確認した上で,エラーが起きたときやその前に何かしらの悪さをするものがあるものと考えて,解決を目指します.

今出ているエラーは

    known_age = age_df[age_df.age.notnull()].values
  File "C:\Python\lib\site-packages\pandas\core\generic.py", line 5575, in __getattr__
    return object.__getattribute__(self, name)
AttributeError: 'DataFrame' object has no attribute 'age'.

のようですね,コード中の

known_age = age_df[age_df.age.notnull()].values

でエラーが起こったようです.

エラー文の一番最後の文AttributeError: 'DataFrame' object has no attribute 'age'.翻訳すると

AttributeError: 'DataFrame' オブジェクトに 'age' 属性がありません。

ですね.ここで言うDataFrameはおそらくage_dfのことを指しており,属性ageにアクセス演算子.を使ってアクセスしようとしてage_df.ageとしたものの,存在しなかったためAttributeErrorが起きたものだと考えられます.

では,今欲しいageがないのですから,age_dfには何が入っているかを確認する必要がありますね.そこで基本となるエラーの解決法として,変数などの値を確認するためにprint文を用いた出力を行います.

エラーが出る前にこのprint文を記述してage_dfの中身を見てみましょう.

  age_df = df[['age', 'pclass', 'sex', 'parch', 'sibsp']]
  age_df = pd.get_dummies(age_df)
+ print(age_df)
  known_age = age_df[age_df.age.notnull()].values

私の環境では

     pclass  parch  sibsp  age_0.3333  age_0.4167  age_0.6667  ...  age_76  age_8  age_9  age_?  sex_female  sex_male
0         1      0      0           0           0           0  ...       0      0      0      0           0         1
1         3      0      1           0           0           0  ...       0      0      0      1           0         1
2         3      0      0           0           0           0  ...       0      0      0      0           0         1
3         3      0      0           0           0           0  ...       0      0      0      0           1         0
4         1      0      0           0           0           0  ...       0      0      0      0           1         0
..      ...    ...    ...         ...         ...         ...  ...     ...    ...    ...    ...         ...       ...
795       3      2      0           0           0           0  ...       0      0      0      1           1         0
796       3      0      1           0           0           0  ...       0      0      0      1           0         1
797       1      2      0           0           0           0  ...       0      0      0      0           1         0
798       2      0      2           0           0           0  ...       0      0      0      0           0         1
799       1      0      0           0           0           0  ...       0      0      0      1           0         1

[800 rows x 96 columns]

と出力されました.testデータとは結合せずに実行したのでそちらの実行結果とは行数が異なりますが,それでも本質的な問題を確認することができました.

具体的には,pd.get_dummies()を行ったことによって,非整数の値を持つ列がOne-Hotエンコーディングのため変更されてしまったという問題です.列ageが存在しなくなっています.

pd.get_dummies()を行う前にageは存在したのでしょうか.確認のためにprint文を挿入して表示させます.

  age_df = df[['age', 'pclass', 'sex', 'parch', 'sibsp']]
+ print(age_df)
  age_df = pd.get_dummies(age_df)
- print(age_df)
  known_age = age_df[age_df.age.notnull()].values

age_dfを表示した結果は

      age  pclass     sex  parch  sibsp
0    32.5       1    male      0      0
1       ?       3    male      0      1
2      33       3    male      0      0
3    30.5       3  female      0      0
4      24       1  female      0      0
..    ...     ...     ...    ...    ...
795     ?       3  female      2      0
796     ?       3    male      0      1
797    31       1  female      2      0
798    32       2    male      0      2
799     ?       1    male      0      0

[800 rows x 5 columns]

のようになりました,この段階では存在していたage列が,次のpd.get_dummies()によってage_ナントカのように展開されていることになりますね.これでは今まで持っていた列ageはもう存在しません.

したがって,AttributeErrorとなります.
必ずしも参考にした記事が当たっているとは限らないので注意しましょう.

3Like

Your answer might help someone💌