Как я могу оптимизировать операции над фреймом данных, избегая циклов в python? - PullRequest
0 голосов
/ 18 апреля 2020

Как мне избежать for index, row in df.iterrows()?

Мои данные выглядят так:

import pandas as pd

keys = ['Address', 'CoordinateX', 'CoordinateY']
values = [['Addr1', 'Addr2', 'Addr3'], [0, 1, 1], [9, 2, 1]]
addresses = dict(zip(keys, values))
df = pd.DataFrame(addresses, columns=keys)
R = 1

df может отображаться как:

  Address  CoordinateX  CoordinateY
0   Addr1            0            9
1   Addr2            1            2
2   Addr3            1            1

Моя задача - добавить новый столбец Counts, в котором хранится количество адресов, расположенных в одной области - круг определенного радиуса R. Однако на последнем кадре данных мне нужен только один представительный адрес для каждой области. Таким образом, результат после вычислений будет:

  Address  CoordinateX  CoordinateY  Counts
0   Addr1            0            9       1
1   Addr2            1            2       2

Изначально у меня был такой код:

df_X = pd.DataFrame() # to fill with counts
for idx, row in df.iterrows():
  x1, y1 = row['CoordinateX'], row['CoordinateY']
  addr_count = 0
  indices = [] # to collect idx2 for dropping in df_X
  df2 = df.copy()
  for idx2, row2 in df2.iterrows():
    x2, y2 = row2['Longitude'], row2['Lattitude']
    distance = math.sqrt((x2-x1)**2 + (y2-y1)**2)
    if distance <= R:
      addr_count += 1
      indices.append(idx2)
  if addr_count > 0:
    row['Count'] = addr_count
    df_X = df_X.append(row, ignore_index=True)
    df.drop(indices, inplace=True) # to exclude the rows in next iteration
df_X.shape

Итак, есть 2 цикла - внешний и внутренний для похожих данных. Поскольку мой исходный фрейм данных содержит пару тысяч строк, я хочу оптимизировать вычисления, используя pd.apply(). Это мой код, который заменяет внутренний l oop на pd.apply:

def count_items(x1, y1, r, df):
  def is_outside(x2, y2): return r < math.sqrt((x2-x1)**2 + (y2-y1)**2)
  df_new = df[df.apply(lambda a: is_outside(a['CoordinateX'], a['CoordinateY']), axis=1)] # new set with distant addresses only
  return df_new, len(df.index) - len(df_new.index)

def get_counted(r, df):
  df_X = pd.DataFrame() # to fill with counts
  for idx, row in df.iterrows():
    x1, y1 = row['CoordinateX'], row['CoordinateX']
    df2 = df.copy()
    df3, addr_count = count_items(x1, y1, r, df2) # df3 contains now only distant addresses
    if addr_count > 0:
      row['Count'] = addr_count
      df_X = df_X.append(row, ignore_index=True)
    df = df3.copy()
  return df_X

df_c = df.copy()
df_addrX = get_counted(R, df_c)

И я понятия не имею, как таким же образом улучшить деталь с внешним l oop. Кто-нибудь может предоставить предложение, пожалуйста? BR, спасибо!

1 Ответ

0 голосов
/ 18 апреля 2020

Вам нужно использовать функцию комбинаций, вы можете найти информацию здесь .

Как говорится, вы можете применять:

комбинаций ('ABCD ', 2)

для получения:

AB A C AD B C BD CD

Первый Аргументом вашей функции является итератор для ваших строк, второй - 2 (вам нужны комбинации из 2 строк).

Вы можете выполнять итерации по индексам строк, поэтому вы можете получить пары индексов строк, например.

После этого, как только у вас появятся пары линий, вы можете отобразить или повторить результат, чтобы применить вашу функцию, которая вычисляет расстояние.

Вы можете складировать все это в DataFrame со следующими столбцами:

FirstRowIndex
SecondRowIndex
Дистанция

Затем просто сделайте groupby() для двух столбцов: FirstRowIndex и Distance, чтобы найти элементы в одной области, удалите те, которые вам не нужны, и сохраните ndexes вам нужно.

Наконец, выберите строки, соответствующие этим индексам в исходном DataFrame, используя, например, df.loc.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...