Записать в датафрейм сроки, с учетом индексов( строки могут быть пустые)
Пример данныхЗадача следующая: есть эксель файл, где пропущены значения адресов, эти ячейки нужны! Нужно перезаписать файл и добавить широту и долготу указанного адреса с учетом пустых ячеек( в которых надо написать "Нет полного адреса")Код ниже работает и выводит адреса,
str_listik = []
Index =[]
for index,line in enumerate(df['Адрес объекта'].head(100)):
if line==1:
#df2.loc[index].Index = index
#print(index)
Index.append(index)
pass
else:
listik = []
for index2, paramValue in enumerate(addr_extractor.find(line).fact.parts):
p=paramValue
i = index2
if p.type=='индекс':
listik.append( p.value)
if p.type=='город':
listik.append( p.value)
if p.type=='улица':
listik.append( p.value)
if p.type=='дом':
listik.append( p.value )
if p.type=='корпус':
listik.append( p.value )
str_listik.append(' '.join(map(str,listik)))
str_listik
НО, когда я применяю функцию
str_listik = []
Index =[]
def parse(string):
if string != 1 or string != None :
listik = []
for index2, paramValue in enumerate(addr_extractor.find(string).fact.parts):
p=paramValue
i = index2
Index.append(index2)
if p.type=='индекс':
listik.append( p.value)
if p.type=='город':
listik.append( p.value)
if p.type=='улица':
listik.append( p.value)
if p.type=='дом':
listik.append( p.value)
if p.type=='корпус':
listik.append( p.value)
#str_listik.append(' '.join(map(str,listik)))
return (' '.join(map(str,listik)))
else:
return None
df2['Address'] = df2['Адрес объекта'].apply(parse)
df2.head(10)
ТО выходит ошибка:
TypeError Traceback (most recent call last)
<ipython-input-9-4dc7bcbf5c95> in <module>
26
27
---> 28 df2['Address'] = df2['Адрес объекта'].apply(parse)
29
30 df2.head(10)
~\anaconda3\lib\site-packages\pandas\core\series.py in apply(self, func, convert_dtype, args, **kwds)
3846 else:
3847 values = self.astype(object).values
-> 3848 mapped = lib.map_infer(values, f, convert=convert_dtype)
3849
3850 if len(mapped) and isinstance(mapped[0], Series):
pandas\_libs\lib.pyx in pandas._libs.lib.map_infer()
<ipython-input-9-4dc7bcbf5c95> in parse(string)
4 if string != 1 or string != None :
5 listik = []
----> 6 for index2, paramValue in enumerate(addr_extractor.find(string).fact.parts):
7 p=paramValue
8 i = index2
~\anaconda3\lib\site-packages\natasha\extractors.py in find(self, text)
74
75 def find(self, text):
---> 76 matches = list(self(text))
77 if not matches:
78 return
~\anaconda3\lib\site-packages\natasha\extractors.py in __call__(self, text)
41
42 def __call__(self, text):
---> 43 for match in self.parser.findall(text):
44 yield adapt_match(match)
45
~\anaconda3\lib\site-packages\yargy\parser.py in findall(self, text)
287
288 def findall(self, text):
--> 289 states = self.matches(text)
290 trees = prepare_trees(states)
291 trees = sorted(trees)
~\anaconda3\lib\site-packages\yargy\parser.py in matches(self, text, all)
274
275 def matches(self, text, all=True):
--> 276 chart = self.chart(text, all=all)
277 return (
278 chart
~\anaconda3\lib\site-packages\yargy\parser.py in chart(self, text, all)
258 tokens = self.tokenizer(text)
259 tokens = self.tagger(tokens)
--> 260 chart = Chart(tokens)
261 for column, next_column in chart:
262 if column.first or all:
~\anaconda3\lib\site-packages\yargy\parser.py in __init__(self, tokens)
25 class Chart(object):
26 def __init__(self, tokens):
---> 27 self.tokens = list(tokens)
28
29 self.columns = [Column(0, None)]
~\anaconda3\lib\site-packages\yargy\tagger.py in __call__(self, tokens)
13 class PassTagger(Tagger):
14 def __call__(self, tokens):
---> 15 for token in tokens:
16 yield token
17
~\anaconda3\lib\site-packages\yargy\tokenizer.py in __call__(self, text)
119 def __call__(self, text):
120 tokens = Tokenizer.__call__(self, text)
--> 121 for token in tokens:
122 if token.type == RUSSIAN:
123 forms = self.morph(token.value)
~\anaconda3\lib\site-packages\yargy\tokenizer.py in __call__(self, text)
97
98 def __call__(self, text):
---> 99 for match in re.finditer(self.regexp, text):
100 name = match.lastgroup
101 value = match.group(0)
~\anaconda3\lib\re.py in finditer(pattern, string, flags)
246
247 Empty matches are included in the result."""
--> 248 return _compile(pattern, flags).finditer(string)
249
250 def compile(pattern, flags=0):
TypeError: expected string or bytes-like object
Ответы (2 шт):
Модуль natasha работает очень медленно, поэтому в решении ниже я показал как это сделать для первых 100 строк файла.
Для ваших реальных данных удалите .iloc[:100] и после запуска скрипта, советую пойти пить кофе или отдохнуть... ;)
def parse_addr(addr_str, addr_extractor=addr_extractor):
ext = addr_extractor.find(addr_str)
if not ext:
return None
return {part.type: part.value for part in ext.fact.parts}
def combine_addr_str(parsed_addr):
if not parsed_addr:
return None
res = ""
if parsed_addr.get("индекс"):
res += parsed_addr.get("индекс")
if parsed_addr.get("город"):
res += (" " if res else "") + parsed_addr.get("город")
if parsed_addr.get("улица"):
res += (" " if res else "") + parsed_addr.get("улица")
if parsed_addr.get("дом"):
res += (" " if res else "") + parsed_addr.get("дом")
if parsed_addr.get("корпус"):
res += (" " if res else "") + parsed_addr.get("корпус")
return res
df = pd.read_excel(filename).iloc[:100] # <--- выбираем первые 100 строк файла
# df = pd.read_excel(filename) # <--- для реальных данных используйте эту строку вместо предыдущей
mask = df["Адрес объекта"].str.len() > 10
df["Address"] = np.nan
df.loc[mask, "Address"] = df.loc[mask, "Адрес объекта"].map(lambda x: combine_addr_str(parse_addr(x)))
результат:
In [118]: df
Out[118]:
Адрес объекта Address
0 ЮВАО, 109469, г Москва, Марьино, ул Марьинский... 109469 Москва Марьинский Парк 21
1 ЗАО, г Москва, Очаково-Матвеевское, ул Очаковс... Москва Очаковская Б 2
2 ТАО, 108828, г. Москва, с Былово, Краснопахорс... 108828 Москва Радужная 7
3 ЗАО, 119571, г Москва, Тропарево-Никулино, пр-... 119571 Москва 94
4 ЮВАО, г Москва, Люблино, ул Ставропольская, д.... Москва Ставропольская 23/1
.. ... ...
95 ЮАО, 115304, г Москва, Царицыно, ул Кантемиров... 115304 Москва Кантемировская 5
96 ЦАО, 105005, г Москва, Басманный, ул Радио, д.... 105005 Москва Радио 10
97 1 NaN
98 1 NaN
99 1 NaN
[100 rows x 2 columns]
Слегка оптимизированный вариант решения - создаем словарь - базу данных уже распарсенных адресов и, в том случае если такой адрес мы уже парсили, просто возвращаем результат из БД:
DB_ADDR = {}
PART_TYPES = [
"индекс", "город",
"улица", "проспект", "шоссе", "переулок", "проезд",
"дом", "корпус"
]
def parse_addr(
addr_str,
addr_extractor=addr_extractor,
db_addr=DB_ADDR,
min_addr_len=10
):
if not addr_str or len(str(addr_str)) < min_addr_len:
return None
ext = addr_extractor.find(addr_str)
if not ext:
return None
addr_str_lower = addr_str.casefold()
if not addr_str_lower in DB_ADDR:
DB_ADDR[addr_str_lower] = {part.type: part.value for part in ext.fact.parts}
return DB_ADDR[addr_str_lower]
def combine_addr_str(parsed_addr, part_types=PART_TYPES):
if not parsed_addr:
return None
return " ".join(
parsed_addr[part_type]
for part_type in part_types
if parsed_addr.get(part_type)
)
def get_addr_str(addr_str):
return combine_addr_str(parse_addr(addr_str, db_addr=DB_ADDR))
df = pd.read_excel(filename).iloc[:100]
df["Address"] = [get_addr_str(x) for x in df["Адрес объекта"]]
результат:
In [289]: df
Out[289]:
Адрес объекта Address
0 ЮВАО, 109469, г Москва, Марьино, ул Марьинский... 109469 Москва Марьинский Парк 21
1 ЗАО, г Москва, Очаково-Матвеевское, ул Очаковс... Москва Очаковская Б 2
2 ТАО, 108828, г. Москва, с Былово, Краснопахорс... 108828 Москва Радужная 7
3 ЗАО, 119571, г Москва, Тропарево-Никулино, пр-... 119571 Москва Вернадского 94
4 ЮВАО, г Москва, Люблино, ул Ставропольская, д.... Москва Ставропольская 23/1
.. ... ...
95 ЮАО, 115304, г Москва, Царицыно, ул Кантемиров... 115304 Москва Кантемировская 5
96 ЦАО, 105005, г Москва, Басманный, ул Радио, д.... 105005 Москва Радио 10
97 1 None
98 1 None
99 1 None
[100 rows x 2 columns]
PS в приведенном в вопросе примере данных ~25% адресов дублируются.