多原子イオンを１つの点に置換した結晶構造の作成ログ

# 最終目標設定
既存の結晶構造から，多原子イオンを１つの点に置換した新しい結晶構造を作成する．


## 準備
以下，２つのファイルを用意する．
* POSCAR: POSCARファイル形式の結晶構造を記したテキストファイル
* POSCAR.nnlist: POSCARファイルから，原子ごとに隣接原子をリストアップしたテキストファイル


## 作成したプログラム
* mk_clusterd_poscar.py
入力：POSCAR, POSCAR.nnlist
出力：POSCAR


## 今回行ったことの流れ
次の流れに沿って，距離が近い原子団を１つの原子に置き換え，新たな結晶構造を作成した．

0．POSCARとPOSCAR.nnlistをPandasのデータフレームとして読み込む．
1．結晶構造の各原子ごとに2Å以内の原子をピックアップする．
※ これは，準備の段階でPOSCAR.nnlistとして作成済み
2．ピックアップされた数が最大の原子をリストアップする．
3．2.でリストアップされた原子と、ともにピックアップされた原子のxyz座標の相加平均をとる．
4．リストアップされた原子の元素種と3.で得た座標を新たな１点とする．
5．2~4で選択されていない原子をリストアップする．
6．4.と5.を組み合わせて，新しい構造を作る．


## 今回行ったことの詳細説明
例として，COD (Crystallography Open Database) から得たBaCO3 (1000033.cif) を用いる．
※https://www.crystallography.net/cod/1000033.cif

### 0．POSCARとPOSCAR.nnlistをPandasのDataFrameとして読み込む．

```python
# Converting a POSCAR file to a DataFrame
from my_package.textfile2df import poscar2df_coords 

df_coords = poscar2df_coords(filename='./POSCAR')
df_coords
```

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>central atom</th>
      <th>x</th>
      <th>y</th>
      <th>z</th>
      <th>Species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0.250000000000000</td>
      <td>0.757000000000000</td>
      <td>0.919000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>0.250000000000000</td>
      <td>0.743000000000000</td>
      <td>0.419000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3</td>
      <td>0.750000000000000</td>
      <td>0.243000000000000</td>
      <td>0.081000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4</td>
      <td>0.750000000000000</td>
      <td>0.257000000000000</td>
      <td>0.581000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>4</th>
      <td>5</td>
      <td>0.250000000000000</td>
      <td>0.901100000000000</td>
      <td>0.912200000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>5</th>
      <td>6</td>
      <td>0.250000000000000</td>
      <td>0.598900000000000</td>
      <td>0.412200000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>6</th>
      <td>7</td>
      <td>0.750000000000000</td>
      <td>0.098900000000000</td>
      <td>0.087800000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>7</th>
      <td>8</td>
      <td>0.750000000000000</td>
      <td>0.401100000000000</td>
      <td>0.587800000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>8</th>
      <td>9</td>
      <td>0.459500000000000</td>
      <td>0.683900000000000</td>
      <td>0.921000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>9</th>
      <td>10</td>
      <td>0.040500000000000</td>
      <td>0.816100000000000</td>
      <td>0.421000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>10</th>
      <td>11</td>
      <td>0.540500000000000</td>
      <td>0.316100000000000</td>
      <td>0.079000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>11</th>
      <td>12</td>
      <td>0.040500000000000</td>
      <td>0.683900000000000</td>
      <td>0.921000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>12</th>
      <td>13</td>
      <td>0.459500000000000</td>
      <td>0.816100000000000</td>
      <td>0.421000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>13</th>
      <td>14</td>
      <td>0.959500000000000</td>
      <td>0.183900000000000</td>
      <td>0.579000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>14</th>
      <td>15</td>
      <td>0.959500000000000</td>
      <td>0.316100000000000</td>
      <td>0.079000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>15</th>
      <td>16</td>
      <td>0.540500000000000</td>
      <td>0.183900000000000</td>
      <td>0.579000000000000</td>
      <td>O</td>
    </tr>
    <tr>
      <th>16</th>
      <td>17</td>
      <td>0.250000000000000</td>
      <td>0.416310000000000</td>
      <td>0.754900000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>17</th>
      <td>18</td>
      <td>0.250000000000000</td>
      <td>0.083690000000000</td>
      <td>0.254900000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>18</th>
      <td>19</td>
      <td>0.750000000000000</td>
      <td>0.583690000000000</td>
      <td>0.245100000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>19</th>
      <td>20</td>
      <td>0.750000000000000</td>
      <td>0.916310000000000</td>
      <td>0.745100000000000</td>
      <td>Ba</td>
    </tr>
  </tbody>
</table>
</div>


```python
# converting POSCAR.nnlist to df_nnlist
from my_package.textfile2df import nnlist2df

df_nnlist = nnlist2df(POSCAR_nnlist='POSCAR.nnlist')
df_nnlist
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>central atom</th>
      <th>neighboring atom</th>
      <th>distance</th>
      <th>X</th>
      <th>Y</th>
      <th>Z</th>
      <th>unitcell_x</th>
      <th>unitcell_y</th>
      <th>unitcell_z</th>
      <th>central species</th>
      <th>neighboring species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>1</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>C</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
      <td>5</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>1.281885</td>
      <td>-0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1</td>
      <td>9</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>-0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>3</th>
      <td>1</td>
      <td>12</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>-0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2</td>
      <td>2</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>C</td>
    </tr>
    <tr>
      <th>5</th>
      <td>2</td>
      <td>6</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>-1.281885</td>
      <td>-0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>6</th>
      <td>2</td>
      <td>10</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>7</th>
      <td>2</td>
      <td>13</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>8</th>
      <td>3</td>
      <td>3</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>C</td>
    </tr>
    <tr>
      <th>9</th>
      <td>3</td>
      <td>7</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>-1.281885</td>
      <td>0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>10</th>
      <td>3</td>
      <td>11</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>11</th>
      <td>3</td>
      <td>15</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>12</th>
      <td>4</td>
      <td>4</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>C</td>
    </tr>
    <tr>
      <th>13</th>
      <td>4</td>
      <td>8</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>1.281885</td>
      <td>0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>14</th>
      <td>4</td>
      <td>14</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>-0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>15</th>
      <td>4</td>
      <td>16</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>-0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>C</td>
      <td>O</td>
    </tr>
    <tr>
      <th>16</th>
      <td>5</td>
      <td>1</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>-1.281885</td>
      <td>0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>17</th>
      <td>5</td>
      <td>5</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>18</th>
      <td>6</td>
      <td>2</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>1.281885</td>
      <td>0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>19</th>
      <td>6</td>
      <td>6</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>20</th>
      <td>7</td>
      <td>3</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>1.281885</td>
      <td>-0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>21</th>
      <td>7</td>
      <td>7</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>22</th>
      <td>8</td>
      <td>4</td>
      <td>1.282630</td>
      <td>0.000000</td>
      <td>-1.281885</td>
      <td>-0.043713</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>23</th>
      <td>8</td>
      <td>8</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>24</th>
      <td>9</td>
      <td>1</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>25</th>
      <td>9</td>
      <td>9</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>26</th>
      <td>10</td>
      <td>2</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>-0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>27</th>
      <td>10</td>
      <td>10</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>28</th>
      <td>11</td>
      <td>3</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>-0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>29</th>
      <td>11</td>
      <td>11</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>30</th>
      <td>12</td>
      <td>1</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>31</th>
      <td>12</td>
      <td>12</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>32</th>
      <td>13</td>
      <td>2</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>-0.650283</td>
      <td>-0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>33</th>
      <td>13</td>
      <td>13</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>34</th>
      <td>14</td>
      <td>4</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>35</th>
      <td>14</td>
      <td>14</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>36</th>
      <td>15</td>
      <td>3</td>
      <td>1.289100</td>
      <td>-1.112990</td>
      <td>-0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>37</th>
      <td>15</td>
      <td>15</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>38</th>
      <td>16</td>
      <td>4</td>
      <td>1.289100</td>
      <td>1.112990</td>
      <td>0.650283</td>
      <td>0.012857</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>C</td>
    </tr>
    <tr>
      <th>39</th>
      <td>16</td>
      <td>16</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>O</td>
      <td>O</td>
    </tr>
    <tr>
      <th>40</th>
      <td>17</td>
      <td>17</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>Ba</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>41</th>
      <td>18</td>
      <td>18</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>Ba</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>42</th>
      <td>19</td>
      <td>19</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>Ba</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>43</th>
      <td>20</td>
      <td>20</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>Ba</td>
      <td>Ba</td>
    </tr>
  </tbody>
</table>
</div>



#### df_nnlist.groupby('central atom').count()['neighboring atom']で最も要素数の多いもののcentral atomをクラスタとして得る．
#### → クラスタ化されたcentral atomを重複削除する　
#### → 新しいcentral atomのリストを得る


```python
print(df_nnlist.groupby('central atom').count()['neighboring atom'])
# これをフィルター化したものがget_elelem_max_num_filter_list
```

    central atom
    1     4
    2     4
    3     4
    4     4
    5     2
    6     2
    7     2
    8     2
    9     2
    10    2
    11    2
    12    2
    13    2
    14    2
    15    2
    16    2
    17    1
    18    1
    19    1
    20    1
    Name: neighboring atom, dtype: int64


#### df_nnlist.groupby('central atom').count()['neighboring atom']で最も要素数の多いもののcentral atomをクラスタとして得る．


```python
import pandas as pd
def get_elem_max_filter(df_nnlist=df_nnlist):
    """
    To get cluster center abs coords from df_coords, Please use this filter.
    
    Input: df_nnlist 
 -> Output: The max number of element 
            in neighboring column of df_nnlist, 
            when df_nnlist groupbyed neighboring column and .count() 
    """
    elem_max_num = df_nnlist.groupby('central atom').count()['neighboring atom'].max()
    elem_max_num_filter = df_nnlist.groupby('central atom').count()['neighboring atom'] == elem_max_num
    # elem_max_num_filter_list = elem_max_num_filter.to_list()
    elem_max_num_filter_list = pd.Series(elem_max_num_filter.to_list())
    return elem_max_num_filter_list
```


```python
get_elem_max_filter()
```




    0      True
    1      True
    2      True
    3      True
    4     False
    5     False
    6     False
    7     False
    8     False
    9     False
    10    False
    11    False
    12    False
    13    False
    14    False
    15    False
    16    False
    17    False
    18    False
    19    False
    dtype: bool



#### メモ：df_coords[elem_max_num_filter_list] によりクラスタ中心の絶対座標を得る



```python
# # 入力値が左側の数値と同じ場合、対応する右側の数値を返す関数
# def get_neighboring_atoms_list(central_atom_id, df_nnlist=df_nnlist):
#     """
#     To get all central atoms of a cluster(:neighbors), Input a number of cluster center element number(:central atom)
    
#     Input: central atom column element In df_nnlist
#  -> Output: All neighboring atom column element that Input(:elemnt) match central atom column element
    
#     param1: Input: central atom column element In df_nnlist
#     """
#     # 左側の列から対応する行を選択し、右側の数値を取得
#     # result = df_nnlist[df_nnlist['central atom'] == input_value]['neighboring atom'].values
#     neighboring_atoms_list = df_nnlist[df_nnlist['central atom'] == central_atom_id]['neighboring atom'].tolist()
#     return neighboring_atoms_list
```


```python
# # 関数をテスト
# get_neighboring_atoms_list(3)
```




    [3, 7, 11, 15]



#### クラスタ中心のcentral atom(id的な番号)の一覧を得る

#### クラスタに選ばれなかった残りのcentral atom (≒id)の一覧を得る関数


```python
def get_all_non_clusterd_atom(df_nnlist=df_nnlist, df_coords=df_coords):
    """
    dependency: get_elem_max_filter(), get_right_value()
    
    To get non-clusterd central atom list, Use this func.
    
    Input: DataFrames
 -> Output: a list 
    
    param1: df_nnlist=df_nnlist
    param2: df_coords=df_coords
    """
    
    # 入力値が左側の数値と同じ場合、対応する右側の数値を返す関数
    def get_neighboring_atoms_list(central_atom_id, df_nnlist=df_nnlist):
        """
        To get all central atoms of a cluster(:neighbors), Input a number of cluster center element number(:central atom)

        Input: central atom column element In df_nnlist
     -> Output: All neighboring atom column element that Input(:elemnt) match central atom column element

        param1: Input: central atom column element In df_nnlist
        """
        # 左側の列から対応する行を選択し、右側の数値を取得
        # result = df_nnlist[df_nnlist['central atom'] == input_value]['neighboring atom'].values
        neighboring_atoms_list = df_nnlist[df_nnlist['central atom'] == central_atom_id]['neighboring atom'].tolist()
        return neighboring_atoms_list
    
    
    elem_max_num_filter = get_elem_max_filter(df_nnlist=df_nnlist)
    # クラスタ(原子団)の中心の原子のid(central atomの値)のリスト
    cluster_central_atom_list = df_coords[elem_max_num_filter]['central atom'].tolist()
    # クラスタ(原子団)に属するすべての原子のid(central atomの値)を取得
    cluster_all_atom_list_duplicated = [get_neighboring_atoms_list(elem) for elem in cluster_central_atom_list]
    # 2重リストを1重リストにflatten
    cluster_all_atom_list_duplicated_flatten = [item for sublist in cluster_all_atom_list_duplicated for item in sublist]
    # flat_listの重複削除
    cluster_all_atom_set = set(cluster_all_atom_list_duplicated_flatten)
    
    # 元のposcarのcentral atomの一覧を取得
    all_central_atom_set = set(df_coords['central atom'].tolist())
    
    # クラスタ(原子団)に属さない原子のid(central atomの値)を取得
    all_non_clusterd_atom_list = list(all_central_atom_set.difference(cluster_all_atom_set))
    
    return all_non_clusterd_atom_list
```


```python
# all_non_clusterd_atom_list = get_all_non_clusterd_atom(df_nnlist=df_nnlist, df_coords=df_coords)
# print(all_non_clusterd_atom_list)
```

### all_non_clusterd_atom_listをフィルター化する関数


```python
# diff_central_atom_filter = df_coords['central atom'].apply(lambda row: row in diff_central_atom_list)
# これをget_diff_central_atom_filterに関数化
```


```python
def get_all_non_clusterd_atom_filter(df_nnlist=df_nnlist, df_coords=df_coords):
    """
    To convert list to filter, Use thie func.
    """
    all_non_clusterd_atom_list = get_all_non_clusterd_atom(df_nnlist=df_nnlist, df_coords=df_coords)
    all_non_clusterd_atom_filter = df_coords['central atom'].apply(lambda row: row in all_non_clusterd_atom_list)
    return all_non_clusterd_atom_filter
```


```python
get_all_non_clusterd_atom_filter()
```




    0     False
    1     False
    2     False
    3     False
    4     False
    5     False
    6     False
    7     False
    8     False
    9     False
    10    False
    11    False
    12    False
    13    False
    14    False
    15    False
    16     True
    17     True
    18     True
    19     True
    Name: central atom, dtype: bool



### df_coords[]で、クラスタの中心の絶対座標のフィルターと重複削除された残りの絶対座標のフィルターを結合してフィルターする

#### 過不足のないcentral atomのfilterが完成


```python
central_atom_filter_fix = get_elem_max_filter() | get_all_non_clusterd_atom_filter()
print(central_atom_filter_fix)
```

    0      True
    1      True
    2      True
    3      True
    4     False
    5     False
    6     False
    7     False
    8     False
    9     False
    10    False
    11    False
    12    False
    13    False
    14    False
    15    False
    16     True
    17     True
    18     True
    19     True
    dtype: bool


#### クラスタ後の新しい絶対座標を得る


```python
df_coords_abs_center = df_coords[central_atom_filter_fix]
df_coords_abs_center
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>central atom</th>
      <th>x</th>
      <th>y</th>
      <th>z</th>
      <th>Species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0.250000000000000</td>
      <td>0.757000000000000</td>
      <td>0.919000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>0.250000000000000</td>
      <td>0.743000000000000</td>
      <td>0.419000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3</td>
      <td>0.750000000000000</td>
      <td>0.243000000000000</td>
      <td>0.081000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4</td>
      <td>0.750000000000000</td>
      <td>0.257000000000000</td>
      <td>0.581000000000000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>16</th>
      <td>17</td>
      <td>0.250000000000000</td>
      <td>0.416310000000000</td>
      <td>0.754900000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>17</th>
      <td>18</td>
      <td>0.250000000000000</td>
      <td>0.083690000000000</td>
      <td>0.254900000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>18</th>
      <td>19</td>
      <td>0.750000000000000</td>
      <td>0.583690000000000</td>
      <td>0.245100000000000</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>19</th>
      <td>20</td>
      <td>0.750000000000000</td>
      <td>0.916310000000000</td>
      <td>0.745100000000000</td>
      <td>Ba</td>
    </tr>
  </tbody>
</table>
</div>



#### クラスタ後の新しい絶対座標を文字列→数値に変換 する関数


```python
def df_elem_str2num(df_coords_abs_center=df_coords_abs_center):
    # 文字列を数値化する
    df_coords_abs_center['x'] = pd.to_numeric(df_coords_abs_center['x'], errors='coerce')
    df_coords_abs_center['y'] = pd.to_numeric(df_coords_abs_center['y'], errors='coerce')
    df_coords_abs_center['z'] = pd.to_numeric(df_coords_abs_center['z'], errors='coerce')
    return df_coords_abs_center
```


```python
df_coords_abs_center = df_elem_str2num(df_coords_abs_center=df_coords_abs_center)
```

    /tmp/ipykernel_2734045/1947849364.py:3: SettingWithCopyWarning: 
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead
    
    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      df_coords_abs_center['x'] = pd.to_numeric(df_coords_abs_center['x'], errors='coerce')
    /tmp/ipykernel_2734045/1947849364.py:4: SettingWithCopyWarning: 
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead
    
    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      df_coords_abs_center['y'] = pd.to_numeric(df_coords_abs_center['y'], errors='coerce')
    /tmp/ipykernel_2734045/1947849364.py:5: SettingWithCopyWarning: 
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead
    
    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      df_coords_abs_center['z'] = pd.to_numeric(df_coords_abs_center['z'], errors='coerce')



```python
# 数値化したクラスタ後の絶対座標
df_coords_abs_center
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>central atom</th>
      <th>x</th>
      <th>y</th>
      <th>z</th>
      <th>Species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0.250000</td>
      <td>0.757000</td>
      <td>0.919000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>0.250000</td>
      <td>0.743000</td>
      <td>0.419000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3</td>
      <td>0.750000</td>
      <td>0.243000</td>
      <td>0.081000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4</td>
      <td>0.750000</td>
      <td>0.257000</td>
      <td>0.581000</td>
      <td>C</td>
    </tr>
    <tr>
      <th>16</th>
      <td>17</td>
      <td>0.250000</td>
      <td>0.416310</td>
      <td>0.754900</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>17</th>
      <td>18</td>
      <td>0.250000</td>
      <td>0.083690</td>
      <td>0.254900</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>18</th>
      <td>19</td>
      <td>0.750000</td>
      <td>0.583690</td>
      <td>0.245100</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>19</th>
      <td>20</td>
      <td>0.750000</td>
      <td>0.916310</td>
      <td>0.745100</td>
      <td>Ba</td>
    </tr>
  </tbody>
</table>
</div>



#### クラスタの相対中心座標を計算


```python
df_nnlist_grouped = df_nnlist.groupby('central atom').mean()
# central atomカラムでgroupby.mean()した後、index列(central atom)をカラムにする   
df_nnlist_grouped = df_nnlist_grouped.reset_index()   
# 意味のないカラムを落とす
df_nnlist_grouped = df_nnlist_grouped[['central atom', 'X', 'Y', 'Z']]
```

    /tmp/ipykernel_2734045/2493061615.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
      df_nnlist_grouped = df_nnlist.groupby('central atom').mean()



```python
# フィルターで必要なクラスタの相対中心座標に絞る
df_cluster_relative_center = df_nnlist_grouped[central_atom_filter_fix]
df_cluster_relative_center
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>central atom</th>
      <th>X</th>
      <th>Y</th>
      <th>Z</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0.000000</td>
      <td>-0.004670</td>
      <td>-0.004500</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>0.000000</td>
      <td>0.004670</td>
      <td>-0.004500</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3</td>
      <td>0.000000</td>
      <td>0.004670</td>
      <td>0.004500</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4</td>
      <td>0.000000</td>
      <td>-0.004670</td>
      <td>0.004500</td>
    </tr>
    <tr>
      <th>16</th>
      <td>17</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
    </tr>
    <tr>
      <th>17</th>
      <td>18</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
    </tr>
    <tr>
      <th>18</th>
      <td>19</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
    </tr>
    <tr>
      <th>19</th>
      <td>20</td>
      <td>0.000000</td>
      <td>0.000000</td>
      <td>0.000000</td>
    </tr>
  </tbody>
</table>
</div>



### 絶対座標 + 相対座標の計算


```python
def get_clusterd_coords(df_abs=df_coords_abs_center, df_relative=df_cluster_relative_center):
    df_coords_x = df_abs['x'] + df_relative['X']
    df_coords_y = df_abs['y'] + df_relative['Y']
    df_coords_z = df_abs['z'] + df_relative['Z']
    df_coords_species = df_abs['Species']

    # カラム名を指定してデータフレームを作成
    df_coords_fix = pd.DataFrame({
        'X': df_coords_x,
        'Y': df_coords_y,
        'Z': df_coords_z,
        'Species': df_coords_species,
    })

    return df_coords_fix
```


```python
df_coords_fix = get_clusterd_coords(df_abs=df_coords_abs_center, df_relative=df_cluster_relative_center)
df_coords_fix
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>X</th>
      <th>Y</th>
      <th>Z</th>
      <th>Species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.250000</td>
      <td>0.752330</td>
      <td>0.914500</td>
      <td>C</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.250000</td>
      <td>0.747670</td>
      <td>0.414500</td>
      <td>C</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0.750000</td>
      <td>0.247670</td>
      <td>0.085500</td>
      <td>C</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.750000</td>
      <td>0.252330</td>
      <td>0.585500</td>
      <td>C</td>
    </tr>
    <tr>
      <th>16</th>
      <td>0.250000</td>
      <td>0.416310</td>
      <td>0.754900</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>17</th>
      <td>0.250000</td>
      <td>0.083690</td>
      <td>0.254900</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>18</th>
      <td>0.750000</td>
      <td>0.583690</td>
      <td>0.245100</td>
      <td>Ba</td>
    </tr>
    <tr>
      <th>19</th>
      <td>0.750000</td>
      <td>0.916310</td>
      <td>0.745100</td>
      <td>Ba</td>
    </tr>
  </tbody>
</table>
</div>




```python
# 元のPOSCARファイルから5行目までを抽出して、新しいPOSCARファイルに書き込む関数
import os
def df2poscar(df=df_coords_fix, original_file="./POSCAR", output_file="gen_data/POSCAR"):
    """
    Writing the DataFrame(:df_coords_fix) to a POSCAR file.
    param1: DataFrame that has 'X', 'Y', 'Z' columns about coords.
    param2: original POSCAR file
    param3: generated POSCAR file
    """
    
    # df_coords_fixを文字列に変換
    def df2str(df):
        df_coords_fix_str = df[['X', 'Y', 'Z']].to_string(header=False, index=False, index_names=False)
        return df_coords_fix_str

    
    # df_coords_fixから元素種を文字列として抽出する関数
    def return_species(df):
        species_line = ' '.join(df['Species'].unique())
        num_line = ' '.join([str(len(df[df['Species'] == specie])) for specie in df['Species'].unique()])
        return species_line + '\n' + num_line

    
    # 元のPOSCARファイルの5行目までを抽出し，新しいファイルに書き込む
    def wirte_header2poscar():
        # 最初の5行を抽出
        with open(original_file, 'r') as infile:
            lines = infile.readlines()[:5]
        # 新しいPOSCARファイルに書き込む
        with open(output_file, 'w') as outfile:
            outfile.writelines(lines)
    
    
    # 新しいPOSCARファイルに書き込んでいく
    def write_species2poscar():
        with open(output_file, 'a') as file:
            # すでに存在するテキストファイルに元素種を追記
            file.write(return_species(df) + '\n')
            # 元素種まで書かれたファイルにDirectという文字をを追記
            file.write('Direct\n')
            # 直交座標を追記
            file.write(df_coords_fix_str + '\n')


    # 関数をcall
    None if os.path.exists("gen_data") else os.makedirs('gen_data')
    df_coords_fix_str = df2str(df)
    wirte_header2poscar()
    write_species2poscar()
    
    print(f"{output_file} にクラスタ化後の内容が書き込まれました。")
```


```python
df2poscar()
```

    gen_data/POSCAR にクラスタ化後の内容が書き込まれました。