Python: Jaki byłby najbardziej efektywny sposób na odczytanie pliku bez domyślnego separatora z milionami rekordów i umieszczenie go w „ramce danych (pandach)”?
Plik to: "file_sd.txt"
A123456MESTUDIANTE 000-12
A123457MPROFESOR 003103
I128734MPROGRAMADOR00-111
A129863FARQUITECTO 00-456
# Fields and position:
# - Activity Indicator : indAct -> 01 Character
# - Person Code : codPer -> 06 Characters
# - Gender (M / F) : sex -> 01 Character
# - Occupation : occupation -> 11 Characters
# - Amount(User format): amount -> 06 Characters (Convert to Number)
Nie jestem pewny. Czy to najlepsza opcja ?:
import pandas as pd
import numpy as np
def stoI(cad):
pos = cad.find("-")
if pos < 0: return int(cad)
return int(cad[pos+1:])*-1
#Read Txt
data = pd.read_csv(r'D:\file_sd.txt',header = None)
data_sep = pd.DataFrame(
{
'indAct' :data[0].str.slice(0,1),
'codPer' :data[0].str.slice(1,7),
'sexo' :data[0].str.slice(7,8),
'ocupac' :data[0].str.slice(8,19),
'monto' :np.vectorize(stoI)(data[0].str.slice(19,25))
})
print(data_sep)
indAct codPer sexo ocupac monto
0 A 123456 M ESTUDIANTE -12
1 A 123457 M PROFESOR 3103
2 I 128734 M PROGRAMADOR -111
3 A 129863 F ARQUITECTO -456
** To rozwiązanie dla 7 milionów wierszy. Wynik: **
%timeit df_slice()
11.1 s ± 166 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
2 odpowiedzi
Masz plik o stałej szerokości, więc powinieneś użyć odpowiedniego Czytelnik pd.read_fwf
. W tym przypadku określimy liczbę znaków, które należą do każdego pola oraz nazwy kolumn.
df = pd.read_fwf('test.txt', header=None, widths=[1, 6, 1, 11, 6])
df.columns = ['indAct' ,'codPer', 'sexo', 'ocupac', 'monto']
# indAct codPer sexo ocupac monto
#0 A 123456 M ESTUDIANTE 000-12
#1 A 123457 M PROFESOR 003103
#2 I 128734 M PROGRAMADOR 00-111
#3 A 129863 F ARQUITECTO 00-456
Teraz możesz ustalić dtypy pól. 'monto'
można przekształcić w liczbę przez usunięcie zer i wywołanie pd.to_numeric
.
df['monto'] = pd.to_numeric(df['monto'].str.strip('0'), errors='coerce')
# indAct codPer sexo ocupac monto
#0 A 123456 M ESTUDIANTE -12
#1 A 123457 M PROFESOR 3103
#2 I 128734 M PROGRAMADOR -111
#3 A 129863 F ARQUITECTO -456
Jak zauważyłeś w komentarzu, może to pozornie wydawać się wolniejsze. Zaletą jest to, że pd.read_fwf
, ponieważ operacja we / wy ma dużo automatycznego czyszczenia danych.
- Prawidłowo wyrzuci kolumny z obiektu, jeśli wszystkie dane są int / float / numeric. W przypadku krojenia ciągów należy ręcznie wpisać kolumny.
- Prawidłowo usunie białe znaki z ciągów w polach, które nie wykorzystują w pełni limitu znaków. Jest to dodatkowy krok, który należy wykonać po krojeniu.
- Prawidłowo wypełnia brakujące dane (wszystkie puste pola)
NaN
. Cięcie strun zachowuje puste napisy i musi być traktowane oddzielnie.pandas
nie rozpoznaje''
jako zerowe, więc tak powinno być właściwie obsługiwane brakujące dane.
W przypadku wielu kolumn obiektów, które w pełni obejmują cały limit znaków, bez brakujących danych, krojenie na ciągi ma tę zaletę. Ale w przypadku ogólnie nieznanego zbioru danych, który musisz pozyskać i ETL, po rozpoczęciu usuwania ciągów znaków i konwersji typów w każdej kolumnie, prawdopodobnie okaże się, że wyznaczone operacje we / wy pandas
są najlepszą opcją.
Możesz wyodrębnić kolumny używające dopasowywania wzorców wyrażeń regularnych. Dla danych, o których mowa, możemy zdefiniować wyrażenie regularne jako:
data[0].str.extract('(?P<indAct>[AI]{1})(?P<codPer>[0-9]{6})(?P<sexo>[MF]{1})(?P<ocupac>[A-Z\s]{11})[0]*[^-|1-9](?P<monto>[0-9-\s]*$)')
Założenie jest takie, że dane są czyste, co może, ale nie musi, być prawidłowym założeniem.
Oto porównanie podejścia w pytaniu i tutaj:
#Data size is 300 rows. ( 4 rows in the question replicated 75 times)
import pandas as pd
import numpy as np
#Returns a number from a string with zeros to the left, For example: stoI('0000-810') return -810
def stoI(cad):
pos = cad.find("-")
if pos < 0: return int(cad)
return int(cad[pos+1:])*-1
data = pd.read_csv('file_sd.txt',header = None)
#Read Txt
def df_slice():
return pd.DataFrame(
{
'indAct' :data[0].str.slice(0,1),
'codPer' :data[0].str.slice(1,7),
'sexo' :data[0].str.slice(7,8),
'ocupac' :data[0].str.slice(8,19),
'monto' :np.vectorize(stoI)(data[0].str.slice(19,25))
})
def df_extract():
return data[0].str.extract('(?P<indAct>[AI]{1})(?P<codPer>[0-9]{6})(?P<sexo>[MF]{1})(?P<ocupac>[A-Z\s]{11})[0]*[^-|1-9](?P<monto>[0-9-\s]*$)')
%timeit df_slice()
1.84 ms ± 30.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit df_extract()
975 µs ± 15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Mam nadzieję że to pomoże!
%timeit df_slice() 10.2 s ± 498 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit df_extract() 12.5 s ± 1.07 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
Podobne pytania
Nowe pytania
python
Python to wielozadaniowy, wielozadaniowy język programowania dynamicznie typowany. Został zaprojektowany tak, aby był szybki do nauczenia się, zrozumienia i użycia oraz wymuszania czystej i jednolitej składni. Należy pamiętać, że Python 2 oficjalnie nie jest obsługiwany od 01-01-2020. Mimo to, w przypadku pytań Pythona specyficznych dla wersji, dodaj znacznik [python-2.7] lub [python-3.x]. Korzystając z wariantu Pythona (np. Jython, PyPy) lub biblioteki (np. Pandas i NumPy), należy umieścić go w tagach.
%timeit df_slice() 10.2 s ± 498 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
def df_fwf(): df = pd.read_fwf(r'D:\file_sin_delimitadores.txt', header=None, widths=[1, 6, 1, 11, 6]) df.columns = ['indAct' ,'codPer', 'sexo', 'ocupac', 'monto'] df['monto'] = pd.to_numeric(df['monto'].str.strip('0'), errors='coerce') return df
%timeit df_fwf() 35 s ± 2.9 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
pd.read_fwf
wykonuje wiele innych operacji czyszczenia danych i konwersji typów, ponieważ jest to operacja we/wy. Jeśli planujesz przeprowadzać jakiekolwiek analizy, prawdopodobnie te operacje i tak będą wymagane wcześniej (konwersja na numeryczne, usuwanie białych znaków z ciągów, reprezentowanie brakujących danych jakoNaN
). Tak więc, chociaż być może udało Ci się uzyskać nieco szybszy sposób dzielenia danych na kolumny, w większości przypadków większość czyszczenia danych została przeniesiona do innego kroku.