<jupyter_start><jupyter_text>Dane: czyszczenie i przygotowanie do analizyPoniżej przedstawiono proces analizy surowych danych oraz ich czyszczenia. W poniższym opracowaniu skorzystano z języka Python 3.8.2 oraz biblioteki pandas. Import bibliotek i wczytanie plikuNa początek zaimportowano biblioteki pandas, numpy oraz regular expressions.<jupyter_code>import pandas as pd
import numpy as np
import re<jupyter_output><empty_output><jupyter_text>Kolejno wczytano plik źródłowy z danymi. Na wstępie wiadomo, że plik jest plikiem w którym separatorem kolumn jest tabulacja.<jupyter_code>df = pd.read_csv('TitanicMess.tsv', sep='\t')<jupyter_output><empty_output><jupyter_text>Wstępna analiza zbioruNastępnie przyjrzano się wielkości zbioru oraz pierwszym kilku początkowym oraz końcowym rekordom pliku, żeby zapoznać się z jego budową.<jupyter_code>df.shape<jupyter_output><empty_output><jupyter_text>Zbiór zawiera 13 kolumn oraz 892 wiersze.<jupyter_code>df.head(10)
df.tail(10)<jupyter_output><empty_output><jupyter_text>Następnie przyjrzano sie typom, do jakich zostały automatycznie zakwalifikowane poszczególne kolumny z danymi.<jupyter_code>df.dtypes<jupyter_output><empty_output><jupyter_text>Kolejno wstępnie przyjrzano się zawartości kolumn o typie object.<jupyter_code>df_describe_object = df.describe(include=['object'])
print(df_describe_object)<jupyter_output>Name   Sex  Age    Ticket  Fare Cabin  \
count                                  892   892  719       892   892   207   
unique                                 889     6   93       680   250   145   
top     Sandstrom, Miss. Marguerite Ru&5$$  male   24  CA. 2343  8,05    G6   
freq                                     3   576   30         7    43     6   

       Embarked     ship  
count       890      892  
unique        6        1  
top           S  Titanic  
freq        643      892<jupyter_text>Oraz kolumnom o typach numerycznych.<jupyter_code>df_describe_number = df.describe(include=[np.number])
print(df_describe_number)<jupyter_output>PassengerId    Survived      Pclass       SibSp       Parch
count   892.000000  892.000000  892.000000  892.000000  892.000000
mean    445.762332    0.386771    2.307175    0.525785    0.371076
std     257.990085    0.487284    0.836750    1.102190    0.768468
min       1.000000    0.000000    1.000000    0.000000    0.000000
25%     223.750000    0.000000    2.000000    0.000000    0.000000
50%     444.500000    0.000000    3.000000    0.000000    0.000000
75%     668.250000    1.000000    3.000000    1.000000    0.000000
max    1000.000000    1.000000    3.000000    8.000000    5.000000<jupyter_text>KolumnySekcja poświęcona analizie zawartości poszczególnych kolumn. Kolumna - PassengerIdPrzyjrzano się kolumnie `PassengerId`, czy jest unikatowa i spełnia warunek stworzenia z niej indeksu.<jupyter_code>df['PassengerId'].value_counts()
df.loc[df['PassengerId'].isin([225,11])]<jupyter_output><empty_output><jupyter_text>Usunięto duplikaty oraz zweryfikowano czy kolumna `PassengerId` zawiera unikatowe wielkości.<jupyter_code>df.drop_duplicates(keep=False,inplace=True)
df.shape
df['PassengerId'].is_unique<jupyter_output><empty_output><jupyter_text>Po weryfikacji zdecydowano sie na ustawienie kolumny `PassengerId` jako kolumny ID dla całego zbioru.<jupyter_code>df.set_index('PassengerId', inplace=True)<jupyter_output><empty_output><jupyter_text>Kolumna - shipUsunięto kolumnę `ship`, która zawierała jednolity wpis na całej swojej zawartości - nie wnosząc żadnych nowych danych do zbioru.<jupyter_code>df.drop(['ship'], inplace=True, axis=1)<jupyter_output><empty_output><jupyter_text>I zweryfikowano usunięcie.<jupyter_code>df.dtypes<jupyter_output><empty_output><jupyter_text>Kolumny - dane jakościoweDla kolumn z automatycznie przydzielonym typem number `Survived`, `Pclass`, a które powinny być zakwafilikowane jako dane jakościowe przekonwertowano typ na `category`. Podobnie postąpiono z kolumną `Sex` oraz `Embarked`.<jupyter_code>df.astype({'Survived': 'category', 'Pclass': 'category', 'Sex': 'category', 'Embarked': 'category'}).dtypes<jupyter_output><empty_output><jupyter_text>Przed dalszymi pracami zweryfikowano ilość brakujących danych w każdej z kolumn.<jupyter_code>df.isnull().sum()<jupyter_output><empty_output><jupyter_text>Kolumna - SurvivedNastępnie przystąpiono do analizy zawartości poszczególnych kolum i oczyszczania danych w nich zawartych. Pracę rozpcoczęto od kolumn, z typem określonym na `category` - weryfikując zawarte dane i dokonując ewentualnych ujednoliceń.<jupyter_code>df['Survived'].unique()<jupyter_output><empty_output><jupyter_text>Dla kolumny `Survived` nie trzeba było przeprowadzać żadnych dodatkowych operacji. Kolumna - Pclass<jupyter_code>df['Pclass'].unique()<jupyter_output><empty_output><jupyter_text>Zawartość kolumny `Pclass` również nie wymagała dodatkowych zmian. Kolumna - Sex<jupyter_code>df['Sex'].unique()<jupyter_output><empty_output><jupyter_text>Dla kolumny `Sex` widzimy kilka różnych unikatowych wartości, mimo spodziewanych 2. Aby usystematyzować dane przystąpiono do podmiany wartości na należące do jednej z dwóch możliwych wartości `['male', 'female']`. Przygotowano słownik zawierający docelową wartość oraz wartości z których należy dokonać poprawki. Po wykonaniu kodu zweryfikowano czy dane w kolumnie są unikatowe.<jupyter_code>replace = {
    'male' : ['malef', 'mal'],
    'female': ['femmale', 'fem']
}
for col in replace:
    for col_repl in replace[col]:
        df.replace({'Sex': col_repl}, col, inplace=True)        
        
df['Sex'].unique()<jupyter_output><empty_output><jupyter_text>Kolumna - EmbarkedKolejno przystąpiono do weryfikacji kolumny `Embarked`.<jupyter_code>df['Embarked'].unique()<jupyter_output><empty_output><jupyter_text>W tym przypadku zauważono kilka wartości, spodziewając się trzech - skrótów nazw portów z których Titanic wypłynął.<jupyter_code>replace = {
    'S' : ['So'],
    'C' : ['Co'],
    'Q' : ['Qe'],
    np.nan: ['nan'],
}

for col in replace:
    for col_repl in replace[col]:
        df.replace({'Embarked': col_repl}, col, inplace=True)        

df['Embarked'].unique()<jupyter_output><empty_output><jupyter_text>Kolumna - NameNastępnie rozpoczęto prace nad kolumnami typu object, zacznając od kolumny `Name`.<jupyter_code>df['Name'].value_counts()<jupyter_output><empty_output><jupyter_text>Kolumna po weryfikacji wygląda na wolną od zduplikowanych wartości. Kolumna - AgeKolejną kolumną na którą zwrócono uwagę była kolumn `Age` - została zakwalifikowana do typu `Object` - mimo że z wcześniejszego przeglądu danych sugerowanym typem powinien być typ liczbowy.<jupyter_code>df['Age'].describe()<jupyter_output><empty_output><jupyter_text>Z podsumowania widać występowanie 93 unikatowych wartości w obrębie tej kolumny.<jupyter_code>df['Age'].unique()<jupyter_output><empty_output><jupyter_text>W tym występowanie kilku wartości, które w stosunku do kolumny zawierajacej wiek są nie odpowiednie np. -3, -12, 4435.W ramach prac nad tą kolumną należy:- przekonwertować wartości, w których występuje `,` lub `.` jako separator dziesiętny do jednolitej postaci,- określić działanie co do wierszy z ujemnym wiekiem - określając czy należy te usunąć, czy też uznać za błąd i zastąpić wartością NaN lub wartością bezwzględną lub też zweryfikować w innych źródłach o ile te będą dostępne- określić działanie co do wierszy z wiekiem przekraczającym znacznie udokumentowaną długość życia człowiekaNa początek przystąpiono do przekonwertowania danych zawierających wiek z `,` do `.`, a następnie do liczb tworząc nową kolumnę `Age_Fixed`.<jupyter_code>df['Age_Fixed'] = [x.replace(',', '.') for x in df['Age'].astype(str)]
sorted(df['Age_Fixed'].unique())
df['Age_Fixed'] = pd.to_numeric(df['Age_Fixed'], errors='coerce')
df['Age_Fixed'].describe()<jupyter_output><empty_output><jupyter_text>Następnie podejrzano wiersze w których występował wiek ujemny.<jupyter_code>df.loc[df['Age_Fixed'] < 0]<jupyter_output><empty_output><jupyter_text>Po jednostkowej weryfikacji obu nazwisk w [dostępnych](https://www.encyclopedia-titanica.org/titanic-victim/sarkis-lahoud-ishaq-mowad.html) [źródłach](https://www.encyclopedia-titanica.org/titanic-survivor/george-brereton.html) zdecydowano się na przypisanie właściwego wieku dla obu przypadków.<jupyter_code>df.loc[508, 'Age_Fixed'] = 37.0
df.loc[523, 'Age_Fixed'] = 35.0
df.loc[[508,523]]<jupyter_output><empty_output><jupyter_text>W następnym kroku sprawdzono przypadki z wiekiem przekraczającym 100 lat.<jupyter_code>df.loc[df['Age_Fixed'] > 100]<jupyter_output><empty_output><jupyter_text>Zweryfikowano dane wymienionych pasażerów w [dostępnych](https://www.encyclopedia-titanica.org/titanic-victim/christo-nenkoff.html) [źródłach](https://www.encyclopedia-titanica.org/titanic-victim/reginald-fenton-butler.html) i poprawiono dane.<jupyter_code>df.loc[224, 'Age_Fixed'] = 22.0
df.loc[667, 'Age_Fixed'] = 25.0
df.loc[[224,667]]<jupyter_output><empty_output><jupyter_text>Usunięto oryginalną kolumnę i zastąpiono ją kolumną z poprawionymi wartościami.<jupyter_code>df.drop(['Age'], inplace=True, axis=1)
df.rename(columns={"Age_Fixed": "Age"}, inplace=True)
df.columns<jupyter_output><empty_output><jupyter_text>Kolumna - TicketW następnym etapie przyjrzano sie kolumnie `Ticket`.<jupyter_code>df['Ticket'].value_counts().head(20)<jupyter_output><empty_output><jupyter_text>W obrębie kolumny Ticket występuje wiele różnych wartości, niektóre powtarzające się - na jednym 'numerze' biletu mogło podróżować kilka osób. Zdecydowano o pozostawieniu kolumny bez dodatkowych zmian, ze względu na brak danych pozwalających na dokonanie skutecznego ujednolicenia numeracji.  Kolumna - FareKolejno przyjrzano sie kolumnie `Fare`, która zawierać powinna wartość opłaty za bilet wyrażoną w funtach. W kontekście historycznym warto zwrócić uwagę na ówczesną budowę waluty, która nie była dziesiętna. Jeden funt dzielił się na 20 szylingów, który to każdy dzielił się na 12 pensów. Stąd wartości biletów nie są zaokrąglane do setnych części.<jupyter_code>sorted(df['Fare'].unique())<jupyter_output><empty_output><jupyter_text>Kolumna powinna zostać automatycznie zakwalifikowana do typu liczbowego, jednakże zastosowano zły znak `,` zamiast `.` jako separator dziesiętny. Wśród wartości zauważyć można też takie, w których występują litery. Na początek przekonwertowano wszystkie `,` na `.`. Oraz następnie do dalszej analizy wyciągnięto wartości nie będące liczbami.<jupyter_code>df['Fare'] = [x.replace(',', '.') for x in df['Fare'].astype(str)]
sorted([s for s in df['Fare'].unique() if not(re.match(r'^-?\d+(?:\.\d+)?$', s))])<jupyter_output><empty_output><jupyter_text>Wartość przypuszczalnie jest literówką, stąd zdecydowano o poprawieniu jej na `15.9`, które pojawia sie już w wartościach.<jupyter_code>df[df['Fare'] =='15.9a']
df.loc[490, 'Fare'] = 15.9
df.loc[490]<jupyter_output><empty_output><jupyter_text>W kolejnym etapie przekonwertowano całą kolumnę na typ numeryczny.<jupyter_code>df['Fare'] = pd.to_numeric(df['Fare'])<jupyter_output><empty_output><jupyter_text>Oraz zweryfikowano czy konwersja przebiegła pomyślnie.<jupyter_code>df['Fare'].describe()<jupyter_output><empty_output><jupyter_text>W kolejnym etapie sprawdzono linię zawierajacą ujemną cenę biletu.<jupyter_code>df[df['Fare'] == -90.0]<jupyter_output><empty_output><jupyter_text>Po zweryfikowaniu pasażera w innym źródle poprawiono wartość do właściwej tj. 90.0.<jupyter_code>df.loc[246, 'Fare'] = 90.0
df.loc[246]<jupyter_output><empty_output><jupyter_text>Kolumna - CabinKolejna kolumną do przygotowania `cabin`.<jupyter_code>sorted(df['Cabin'].unique().astype(str))<jupyter_output><empty_output><jupyter_text>Zawartość kolumny pozostanie bez zmian, w kontekście dalszej analizy i ilości stwierdzonych braków w tej kolumnie (patrz początek analizy) można zastanowić się nad pominięciem kolumny jako, że zawartość może zaburzyć wyniki.  Kolumna - SibSpKolumna zawiera wartości liczbowe wskazujące na ilość rodzeństwa/małżonków na pokładzie. Spodziewane są wartości liczbowe całkowite.<jupyter_code>sorted(df['SibSp'].unique())<jupyter_output><empty_output><jupyter_text>Wartości zgadzają się z przewidywaniami. Dalsza obróbka nie jest wymagana, w trakcie analizy można zweryfikować np. po korelacji z numerami biletów, nazwiskami czy liczba osób spokrewnionych oddaje stan faktyczny w zbiorze danych.  Kolumna - ParchKolumna zawiera wartości liczbowe wskazujące na ilość rodziców/dzieci na pokładzie w relacji do danej osoby. Spodziewane dane powinny być liczbami całkowitymi.<jupyter_code>sorted(df['Parch'].unique())<jupyter_output><empty_output><jupyter_text>Wartości zgadzają się z przewidywaniami. Dalsza obróbka nie jest wymagana, w trakcie analizy można zweryfikować np. po korelacji z numerami biletów, nazwiskami czy liczba osób spokrewnionych oddaje stan faktyczny w zbiorze danych.  PodsumowaniePo zakończonych pracach przejrzano raz jeszcze poszczególne kolumny danych.<jupyter_code>df.dtypes<jupyter_output><empty_output><jupyter_text>Typy kolumn oraz ich zawartość zgadzają się z założeniami (kolumny `Survived` oraz `Pclass` zostały ponownie automatycznie przekwalifikowane do innych typów).Zawartość przedstawia się następująco.<jupyter_code>df_describe_object = df.describe(exclude=[np.number])
print(df_describe_object)
df.describe()<jupyter_output><empty_output><jupyter_text>Przykładowe kilka losowych rekordów:<jupyter_code>df.sample(20)<jupyter_output><empty_output><jupyter_text>Eksport plikuPlik zapisano do formatu .csv.<jupyter_code>df.to_csv("TitanicCleaned.tsv", sep="\t")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0964821901677951, 'fraction_numerical': 0.035840447453635564, 'mean_word_length': 6.019111570247934, 'pattern_counts': {'":': 1, '<': 176, '<?xml version=': 0, '>': 176, 'https://': 4, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '44', 'dir': 'jupyter-structured-clean-dedup', 'id': '600839', 'license': 'MIT', 'n_tokens_mistral': 5863, 'n_tokens_neox': 5706, 'n_words': 1122, 'path': 'TitanicCleanup.ipynb', 'repo_name': 'proenix/us3siwb', 'size': '13588'}